+variables:
+ # On stable branches this is changed by later rules. Should also
+ # be overridden per pipeline if running pipelines concurrently
+ # for different branches in contributor forks.
+ QEMU_CI_CONTAINER_TAG: latest
+
+ # For purposes of CI rules, upstream is the gitlab.com/qemu-project
+ # namespace. When testing CI, it might be usefult to override this
+ # to point to a fork repo
+ QEMU_CI_UPSTREAM: qemu-project
+
# The order of rules defined here is critically important.
# They are evaluated in order and first match wins.
#
# Thus we group them into a number of stages, ordered from
# most restrictive to least restrictive
#
+# For pipelines running for stable "staging-X.Y" branches
+# we must override QEMU_CI_CONTAINER_TAG
+#
.base_job_template:
variables:
# Each script line from will be in a collapsible section in the job output
# want jobs to run
#############################################################
+ # Never run jobs upstream on stable branch, staging branch jobs already ran
+ - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /^stable-/'
+ when: never
+
+ # Never run jobs upstream on tags, staging branch jobs already ran
+ - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG'
+ when: never
+
# Cirrus jobs can't run unless the creds / target repo are set
- if: '$QEMU_JOB_CIRRUS && ($CIRRUS_GITHUB_REPO == null || $CIRRUS_API_TOKEN == null)'
when: never
# Publishing jobs should only run on the default branch in upstream
- - if: '$QEMU_JOB_PUBLISH == "1" && $CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
+ - if: '$QEMU_JOB_PUBLISH == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
when: never
# Non-publishing jobs should only run on staging branches in upstream
- - if: '$QEMU_JOB_PUBLISH != "1" && $CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH !~ /staging/'
+ - if: '$QEMU_JOB_PUBLISH != "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH !~ /staging/'
when: never
# Jobs only intended for forks should always be skipped on upstream
- - if: '$QEMU_JOB_ONLY_FORKS == "1" && $CI_PROJECT_NAMESPACE == "qemu-project"'
+ - if: '$QEMU_JOB_ONLY_FORKS == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM'
when: never
# Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set
- - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
+ - 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-project"'
+ - if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: never
#############################################################
# Optional jobs should not be run unless manually triggered
+ - if: '$QEMU_JOB_OPTIONAL && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
+ when: manual
+ allow_failure: true
+ variables:
+ QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
+
- if: '$QEMU_JOB_OPTIONAL'
when: manual
allow_failure: true
# Skipped jobs should not be run unless manually triggered
+ - if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
+ when: manual
+ allow_failure: true
+ variables:
+ QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
+
- if: '$QEMU_JOB_SKIPPED'
when: manual
allow_failure: true
# Avocado jobs can be manually start in forks if $QEMU_CI_AVOCADO_TESTING is unset
- - if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != "qemu-project"'
+ - if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: manual
allow_failure: true
# Forks pipeline jobs don't start automatically unless
# QEMU_CI=2 is set
- - if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
+ - if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
+ when: manual
+
+ # Upstream pipeline jobs start automatically unless told not to
+ # by setting QEMU_CI=1
+ - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
+ when: manual
+ variables:
+ QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
+
+ - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM'
when: manual
# Jobs can run if any jobs they depend on were successful
+ - if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
+ when: on_success
+ variables:
+ QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
+
- when: on_success
.native_build_job_template:
extends: .base_job_template
stage: build
- image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
before_script:
- JOBS=$(expr $(nproc) + 1)
script:
.common_test_job_template:
extends: .base_job_template
stage: test
- image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
script:
- scripts/git-submodule.sh update roms/SLOF
- meson subprojects download $(cd build/subprojects && echo *)
build-libvhost-user:
extends: .base_job_template
stage: build
- image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
+ image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG
needs:
job: amd64-fedora-container
script:
# of what topic branch they're currently using
pages:
extends: .base_job_template
- image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest
+ image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:$QEMU_CI_CONTAINER_TAG
stage: test
needs:
- job: build-tools-and-docs-debian
services:
- docker:dind
before_script:
- - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
+ - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:$QEMU_CI_CONTAINER_TAG"
+ # Always ':latest' because we always use upstream as a common cache source
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest"
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
- until docker info; do sleep 1; done
.cross_system_build_job:
extends: .base_job_template
stage: build
- image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
timeout: 80m
script:
- mkdir build
.cross_accel_build_job:
extends: .base_job_template
stage: build
- image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
timeout: 30m
script:
- mkdir build
.cross_user_build_job:
extends: .base_job_template
stage: build
- image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
script:
- mkdir build
- cd build
check-python-minreqs:
extends: .base_job_template
stage: test
- image: $CI_REGISTRY_IMAGE/qemu/python:latest
+ image: $CI_REGISTRY_IMAGE/qemu/python:$QEMU_CI_CONTAINER_TAG
script:
- make -C python check-minreqs
variables:
check-python-tox:
extends: .base_job_template
stage: test
- image: $CI_REGISTRY_IMAGE/qemu/python:latest
+ image: $CI_REGISTRY_IMAGE/qemu/python:$QEMU_CI_CONTAINER_TAG
script:
- make -C python check-tox
variables:
needs: []
stage: build
timeout: 80m
+ artifacts:
+ name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+ expire_in: 7 days
+ paths:
+ - build/meson-logs/testlog.txt
+ reports:
+ junit: "build/meson-logs/testlog.junit.xml"
before_script:
- If ( !(Test-Path -Path msys64\var\cache ) ) {
mkdir msys64\var\cache
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYSTEM = 'MINGW64' # Start a 64-bit MinGW environment
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- - mkdir output
- - cd output
+ - mkdir build
+ - cd build
# Note: do not remove "--without-default-devices"!
# commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices"
# changed to compile QEMU with the --without-default-devices switch
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYSTEM = 'MINGW32' # Start a 32-bit MinGW environment
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- - mkdir output
- - cd output
+ - mkdir build
+ - cd build
- ..\msys64\usr\bin\bash -lc '../configure --target-list=ppc64-softmmu
--enable-fdt=system'
- ..\msys64\usr\bin\bash -lc 'make'
R: Cédric Le Goater <clg@kaod.org>
R: David Gibson <david@gibson.dropbear.id.au>
R: Greg Kurz <groug@kaod.org>
+R: Nicholas Piggin <npiggin@gmail.com>
L: qemu-ppc@nongnu.org
S: Odd Fixes
F: target/ppc/
PowerNV (Non-Virtualized)
M: Cédric Le Goater <clg@kaod.org>
+R: Frédéric Barrat <fbarrat@linux.ibm.com>
+R: Nicholas Piggin <npiggin@gmail.com>
L: qemu-ppc@nongnu.org
S: Odd Fixes
F: docs/system/ppc/powernv.rst
XIVE
M: Cédric Le Goater <clg@kaod.org>
+R: Frédéric Barrat <fbarrat@linux.ibm.com>
L: qemu-ppc@nongnu.org
S: Odd Fixes
F: hw/*/*xive*
F: docs/sphinx/dbus*
F: docs/sphinx/fakedbusdoc.py
F: tests/qtest/dbus*
+F: scripts/xml-preprocess*
Seccomp
M: Daniel P. Berrange <berrange@redhat.com>
@if test -f meson-private/coredata.dat; then \
./config.status --skip-meson; \
else \
- ./config.status && touch build.ninja.stamp; \
+ ./config.status; \
fi
# 2. meson.stamp exists if meson has run at least once (so ninja reconfigure
# works), but otherwise never needs to be updated
+
meson-private/coredata.dat: meson.stamp
meson.stamp: config-host.mak
@touch meson.stamp
-# 3. ensure generated build files are up-to-date
+# 3. ensure meson-generated build files are up-to-date
ifneq ($(NINJA),)
Makefile.ninja: build.ninja
endif
ifneq ($(MESON),)
-# A separate rule is needed for Makefile dependencies to avoid -n
+# The path to meson always points to pyvenv/bin/meson, but the absolute
+# paths could change. In that case, force a regeneration of build.ninja.
+# Note that this invocation of $(NINJA), just like when Make rebuilds
+# Makefiles, does not include -n.
build.ninja: build.ninja.stamp
$(build-files):
build.ninja.stamp: meson.stamp $(build-files)
- $(MESON) setup --reconfigure $(SRC_PATH) && touch $@
+ @if test "$$(cat build.ninja.stamp)" = "$(MESON)" && test -n "$(NINJA)"; then \
+ $(NINJA) build.ninja; \
+ else \
+ echo "$(MESON) setup --reconfigure $(SRC_PATH)"; \
+ $(MESON) setup --reconfigure $(SRC_PATH); \
+ fi && echo "$(MESON)" > $@
Makefile.mtest: build.ninja scripts/mtest2make.py
$(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@
"kvm_init_vcpu: kvm_arch_init_vcpu failed (%lu)",
kvm_arch_vcpu_id(cpu));
}
+ cpu->kvm_vcpu_stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
+
err:
return ret;
}
/* Read stats header */
kvm_stats_header = &descriptors->kvm_stats_header;
- ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header));
+ ret = pread(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header), 0);
if (ret != sizeof(*kvm_stats_header)) {
error_setg(errp, "KVM stats: failed to read stats header: "
"expected %zu actual %zu",
}
static void query_stats(StatsResultList **result, StatsTarget target,
- strList *names, int stats_fd, Error **errp)
+ strList *names, int stats_fd, CPUState *cpu,
+ Error **errp)
{
struct kvm_stats_desc *kvm_stats_desc;
struct kvm_stats_header *kvm_stats_header;
break;
case STATS_TARGET_VCPU:
add_stats_entry(result, STATS_PROVIDER_KVM,
- current_cpu->parent_obj.canonical_path,
+ cpu->parent_obj.canonical_path,
stats_list);
break;
default:
add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list);
}
-static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data)
+static void query_stats_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args)
{
- StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr;
- int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
+ int stats_fd = cpu->kvm_vcpu_stats_fd;
Error *local_err = NULL;
if (stats_fd == -1) {
return;
}
query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU,
- kvm_stats_args->names, stats_fd, kvm_stats_args->errp);
- close(stats_fd);
+ kvm_stats_args->names, stats_fd, cpu,
+ kvm_stats_args->errp);
}
-static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data)
+static void query_stats_schema_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args)
{
- StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr;
- int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
+ int stats_fd = cpu->kvm_vcpu_stats_fd;
Error *local_err = NULL;
if (stats_fd == -1) {
}
query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd,
kvm_stats_args->errp);
- close(stats_fd);
}
static void query_stats_cb(StatsResultList **result, StatsTarget target,
error_setg_errno(errp, errno, "KVM stats: ioctl failed");
return;
}
- query_stats(result, target, names, stats_fd, errp);
+ query_stats(result, target, names, stats_fd, NULL, errp);
close(stats_fd);
break;
}
if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) {
continue;
}
- run_on_cpu(cpu, query_stats_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args));
+ query_stats_vcpu(cpu, &stats_args);
}
break;
}
if (first_cpu) {
stats_args.result.schema = result;
stats_args.errp = errp;
- run_on_cpu(first_cpu, query_stats_schema_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args));
+ query_stats_schema_vcpu(first_cpu, &stats_args);
}
}
{
}
-void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
+void tlb_set_dirty(CPUState *cpu, vaddr vaddr)
{
}
{
}
-int probe_access_flags(CPUArchState *env, target_ulong addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr)
{
g_assert_not_reached();
}
-void *probe_access(CPUArchState *env, target_ulong addr, int size,
+void *probe_access(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
/* Handled by hardware accelerator. */
}
struct tb_desc {
- target_ulong pc;
- target_ulong cs_base;
+ vaddr pc;
+ uint64_t cs_base;
CPUArchState *env;
tb_page_addr_t page_addr0;
uint32_t flags;
return true;
} else {
tb_page_addr_t phys_page1;
- target_ulong virt_page1;
+ vaddr virt_page1;
/*
* We know that the first page matched, and an otherwise valid TB
return false;
}
-static TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
- target_ulong cs_base, uint32_t flags,
+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;
}
/* Might cause an exception, so have a longjmp destination ready */
-static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
- target_ulong cs_base,
- uint32_t flags, uint32_t cflags)
+static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc,
+ uint64_t cs_base, uint32_t flags,
+ uint32_t cflags)
{
TranslationBlock *tb;
CPUJumpCache *jc;
return tb;
}
-static void log_cpu_exec(target_ulong pc, CPUState *cpu,
+static void log_cpu_exec(vaddr pc, CPUState *cpu,
const TranslationBlock *tb)
{
if (qemu_log_in_addr_range(pc)) {
qemu_log_mask(CPU_LOG_EXEC,
"Trace %d: %p [%08" PRIx64
- "/" TARGET_FMT_lx "/%08x/%08x] %s\n",
+ "/%" VADDR_PRIx "/%08x/%08x] %s\n",
cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc,
tb->flags, tb->cflags, lookup_symbol(pc));
}
}
-static bool check_for_breakpoints_slow(CPUState *cpu, target_ulong pc,
+static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc,
uint32_t *cflags)
{
CPUBreakpoint *bp;
return false;
}
-static inline bool check_for_breakpoints(CPUState *cpu, target_ulong pc,
+static inline bool check_for_breakpoints(CPUState *cpu, vaddr pc,
uint32_t *cflags)
{
return unlikely(!QTAILQ_EMPTY(&cpu->breakpoints)) &&
{
CPUState *cpu = env_cpu(env);
TranslationBlock *tb;
- target_ulong cs_base, pc;
+ vaddr pc;
+ uint64_t cs_base;
uint32_t flags, cflags;
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
cc->set_pc(cpu, last_tb->pc);
}
if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
- target_ulong pc = log_pc(cpu, last_tb);
+ vaddr pc = log_pc(cpu, last_tb);
if (qemu_log_in_addr_range(pc)) {
- qemu_log("Stopped execution of TB chain before %p ["
- TARGET_FMT_lx "] %s\n",
+ qemu_log("Stopped execution of TB chain before %p [%"
+ VADDR_PRIx "] %s\n",
last_tb->tc.ptr, pc, lookup_symbol(pc));
}
}
{
CPUArchState *env = cpu->env_ptr;
TranslationBlock *tb;
- target_ulong cs_base, pc;
+ vaddr pc;
+ uint64_t cs_base;
uint32_t flags, cflags;
int tb_exit;
}
static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
- target_ulong pc,
- TranslationBlock **last_tb, int *tb_exit)
+ vaddr pc, TranslationBlock **last_tb,
+ int *tb_exit)
{
int32_t insns_left;
while (!cpu_handle_interrupt(cpu, &last_tb)) {
TranslationBlock *tb;
- target_ulong cs_base, pc;
+ vaddr pc;
+ uint64_t cs_base;
uint32_t flags, cflags;
cpu_get_tb_cpu_state(cpu->env_ptr, &pc, &cs_base, &flags);
desc->window_max_entries = max_entries;
}
-static void tb_jmp_cache_clear_page(CPUState *cpu, target_ulong page_addr)
+static void tb_jmp_cache_clear_page(CPUState *cpu, vaddr page_addr)
{
CPUJumpCache *jc = cpu->tb_jmp_cache;
int i, i0;
}
static bool tlb_hit_page_mask_anyprot(CPUTLBEntry *tlb_entry,
- target_ulong page, target_ulong mask)
+ vaddr page, vaddr mask)
{
page &= mask;
mask &= TARGET_PAGE_MASK | TLB_INVALID_MASK;
page == (tlb_entry->addr_code & mask));
}
-static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry,
- target_ulong page)
+static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, vaddr page)
{
return tlb_hit_page_mask_anyprot(tlb_entry, page, -1);
}
/* Called with tlb_c.lock held */
static bool tlb_flush_entry_mask_locked(CPUTLBEntry *tlb_entry,
- target_ulong page,
- target_ulong mask)
+ vaddr page,
+ vaddr mask)
{
if (tlb_hit_page_mask_anyprot(tlb_entry, page, mask)) {
memset(tlb_entry, -1, sizeof(*tlb_entry));
return false;
}
-static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry,
- target_ulong page)
+static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, vaddr page)
{
return tlb_flush_entry_mask_locked(tlb_entry, page, -1);
}
/* Called with tlb_c.lock held */
static void tlb_flush_vtlb_page_mask_locked(CPUArchState *env, int mmu_idx,
- target_ulong page,
- target_ulong mask)
+ vaddr page,
+ vaddr mask)
{
CPUTLBDesc *d = &env_tlb(env)->d[mmu_idx];
int k;
}
static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx,
- target_ulong page)
+ vaddr page)
{
tlb_flush_vtlb_page_mask_locked(env, mmu_idx, page, -1);
}
-static void tlb_flush_page_locked(CPUArchState *env, int midx,
- target_ulong page)
+static void tlb_flush_page_locked(CPUArchState *env, int midx, vaddr page)
{
- target_ulong lp_addr = env_tlb(env)->d[midx].large_page_addr;
- target_ulong lp_mask = env_tlb(env)->d[midx].large_page_mask;
+ vaddr lp_addr = env_tlb(env)->d[midx].large_page_addr;
+ vaddr lp_mask = env_tlb(env)->d[midx].large_page_mask;
/* Check if we need to flush due to large pages. */
if ((page & lp_mask) == lp_addr) {
- tlb_debug("forcing full flush midx %d ("
- TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
+ tlb_debug("forcing full flush midx %d (%"
+ VADDR_PRIx "/%" VADDR_PRIx ")\n",
midx, lp_addr, lp_mask);
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
} else {
* at @addr from the tlbs indicated by @idxmap from @cpu.
*/
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap)
{
CPUArchState *env = cpu->env_ptr;
assert_cpu_is_self(cpu);
- tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%x\n", addr, idxmap);
+ tlb_debug("page addr: %" VADDR_PRIx " mmu_map:0x%x\n", addr, idxmap);
qemu_spin_lock(&env_tlb(env)->c.lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu,
run_on_cpu_data data)
{
- target_ulong addr_and_idxmap = (target_ulong) data.target_ptr;
- target_ulong addr = addr_and_idxmap & TARGET_PAGE_MASK;
+ vaddr addr_and_idxmap = data.target_ptr;
+ vaddr addr = 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 {
- target_ulong addr;
+ vaddr addr;
uint16_t idxmap;
} TLBFlushPageByMMUIdxData;
g_free(d);
}
-void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap)
+void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap)
{
- tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%" PRIx16 "\n", addr, idxmap);
+ tlb_debug("addr: %" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap);
/* This should already be page aligned */
addr &= TARGET_PAGE_MASK;
}
}
-void tlb_flush_page(CPUState *cpu, target_ulong addr)
+void tlb_flush_page(CPUState *cpu, vaddr addr)
{
tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS);
}
-void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr,
+void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, vaddr addr,
uint16_t idxmap)
{
- tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap);
+ tlb_debug("addr: %" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
/* This should already be page aligned */
addr &= TARGET_PAGE_MASK;
tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap);
}
-void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr)
+void tlb_flush_page_all_cpus(CPUState *src, vaddr addr)
{
tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS);
}
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap)
{
- tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap);
+ tlb_debug("addr: %" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
/* This should already be page aligned */
addr &= TARGET_PAGE_MASK;
}
}
-void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr)
+void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr)
{
tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS);
}
static void tlb_flush_range_locked(CPUArchState *env, int midx,
- target_ulong addr, target_ulong len,
+ vaddr addr, vaddr len,
unsigned bits)
{
CPUTLBDesc *d = &env_tlb(env)->d[midx];
CPUTLBDescFast *f = &env_tlb(env)->f[midx];
- target_ulong mask = MAKE_64BIT_MASK(0, bits);
+ vaddr mask = MAKE_64BIT_MASK(0, bits);
/*
* If @bits is smaller than the tlb size, there may be multiple entries
*/
if (mask < f->mask || len > f->mask) {
tlb_debug("forcing full flush midx %d ("
- TARGET_FMT_lx "/" TARGET_FMT_lx "+" TARGET_FMT_lx ")\n",
+ "%" VADDR_PRIx "/%" VADDR_PRIx "+%" VADDR_PRIx ")\n",
midx, addr, mask, len);
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
return;
*/
if (((addr + len - 1) & d->large_page_mask) == d->large_page_addr) {
tlb_debug("forcing full flush midx %d ("
- TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
+ "%" VADDR_PRIx "/%" VADDR_PRIx ")\n",
midx, d->large_page_addr, d->large_page_mask);
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
return;
}
- for (target_ulong i = 0; i < len; i += TARGET_PAGE_SIZE) {
- target_ulong page = addr + i;
+ for (vaddr i = 0; i < len; i += TARGET_PAGE_SIZE) {
+ vaddr page = addr + i;
CPUTLBEntry *entry = tlb_entry(env, midx, page);
if (tlb_flush_entry_mask_locked(entry, page, mask)) {
}
typedef struct {
- target_ulong addr;
- target_ulong len;
+ vaddr addr;
+ vaddr len;
uint16_t idxmap;
uint16_t bits;
} TLBFlushRangeData;
assert_cpu_is_self(cpu);
- tlb_debug("range:" TARGET_FMT_lx "/%u+" TARGET_FMT_lx " mmu_map:0x%x\n",
+ tlb_debug("range: %" VADDR_PRIx "/%u+%" VADDR_PRIx " mmu_map:0x%x\n",
d.addr, d.bits, d.len, d.idxmap);
qemu_spin_lock(&env_tlb(env)->c.lock);
* overlap the flushed pages, which includes the previous.
*/
d.addr -= TARGET_PAGE_SIZE;
- for (target_ulong i = 0, n = d.len / TARGET_PAGE_SIZE + 1; i < n; i++) {
+ for (vaddr i = 0, n = d.len / TARGET_PAGE_SIZE + 1; i < n; i++) {
tb_jmp_cache_clear_page(cpu, d.addr);
d.addr += TARGET_PAGE_SIZE;
}
g_free(d);
}
-void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr,
- target_ulong len, uint16_t idxmap,
+void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
+ vaddr len, uint16_t idxmap,
unsigned bits)
{
TLBFlushRangeData d;
}
}
-void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr,
+void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
uint16_t idxmap, unsigned bits)
{
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
}
void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu,
- target_ulong addr, target_ulong len,
+ vaddr addr, vaddr len,
uint16_t idxmap, unsigned bits)
{
TLBFlushRangeData d;
}
void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu,
- target_ulong addr,
- uint16_t idxmap, unsigned bits)
+ vaddr addr, uint16_t idxmap,
+ unsigned bits)
{
tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE,
idxmap, bits);
}
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
- target_ulong addr,
- target_ulong len,
+ vaddr addr,
+ vaddr len,
uint16_t idxmap,
unsigned bits)
{
}
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap,
unsigned bits)
{
/* Called with tlb_c.lock held */
static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry,
- target_ulong vaddr)
+ vaddr addr)
{
- if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) {
- tlb_entry->addr_write = vaddr;
+ if (tlb_entry->addr_write == (addr | TLB_NOTDIRTY)) {
+ tlb_entry->addr_write = addr;
}
}
/* update the TLB corresponding to virtual page vaddr
so that it is no longer dirty */
-void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
+void tlb_set_dirty(CPUState *cpu, vaddr addr)
{
CPUArchState *env = cpu->env_ptr;
int mmu_idx;
assert_cpu_is_self(cpu);
- vaddr &= TARGET_PAGE_MASK;
+ addr &= TARGET_PAGE_MASK;
qemu_spin_lock(&env_tlb(env)->c.lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
- tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, vaddr), vaddr);
+ tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, addr), addr);
}
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
int k;
for (k = 0; k < CPU_VTLB_SIZE; k++) {
- tlb_set_dirty1_locked(&env_tlb(env)->d[mmu_idx].vtable[k], vaddr);
+ tlb_set_dirty1_locked(&env_tlb(env)->d[mmu_idx].vtable[k], addr);
}
}
qemu_spin_unlock(&env_tlb(env)->c.lock);
/* Our TLB does not support large pages, so remember the area covered by
large pages and trigger a full TLB flush if these are invalidated. */
static void tlb_add_large_page(CPUArchState *env, int mmu_idx,
- target_ulong vaddr, target_ulong size)
+ vaddr addr, uint64_t size)
{
- target_ulong lp_addr = env_tlb(env)->d[mmu_idx].large_page_addr;
- target_ulong lp_mask = ~(size - 1);
+ vaddr lp_addr = env_tlb(env)->d[mmu_idx].large_page_addr;
+ vaddr lp_mask = ~(size - 1);
- if (lp_addr == (target_ulong)-1) {
+ if (lp_addr == (vaddr)-1) {
/* No previous large page. */
- lp_addr = vaddr;
+ lp_addr = addr;
} else {
/* Extend the existing region to include the new page.
This is a compromise between unnecessary flushes and
the cost of maintaining a full variable size TLB. */
lp_mask &= env_tlb(env)->d[mmu_idx].large_page_mask;
- while (((lp_addr ^ vaddr) & lp_mask) != 0) {
+ while (((lp_addr ^ addr) & lp_mask) != 0) {
lp_mask <<= 1;
}
}
env_tlb(env)->d[mmu_idx].large_page_mask = lp_mask;
}
+static inline void tlb_set_compare(CPUTLBEntryFull *full, CPUTLBEntry *ent,
+ target_ulong address, int flags,
+ MMUAccessType access_type, bool enable)
+{
+ if (enable) {
+ address |= flags & TLB_FLAGS_MASK;
+ flags &= TLB_SLOW_FLAGS_MASK;
+ if (flags) {
+ address |= TLB_FORCE_SLOW;
+ }
+ } else {
+ address = -1;
+ flags = 0;
+ }
+ ent->addr_idx[access_type] = address;
+ full->slow_flags[access_type] = flags;
+}
+
/*
* Add a new TLB entry. At most one entry for a given virtual address
* is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
* critical section.
*/
void tlb_set_page_full(CPUState *cpu, int mmu_idx,
- target_ulong vaddr, CPUTLBEntryFull *full)
+ vaddr addr, CPUTLBEntryFull *full)
{
CPUArchState *env = cpu->env_ptr;
CPUTLB *tlb = env_tlb(env);
CPUTLBDesc *desc = &tlb->d[mmu_idx];
MemoryRegionSection *section;
- unsigned int index;
- target_ulong address;
- target_ulong write_address;
+ unsigned int index, read_flags, write_flags;
uintptr_t addend;
CPUTLBEntry *te, tn;
hwaddr iotlb, xlat, sz, paddr_page;
- target_ulong vaddr_page;
+ vaddr addr_page;
int asidx, wp_flags, prot;
bool is_ram, is_romd;
sz = TARGET_PAGE_SIZE;
} else {
sz = (hwaddr)1 << full->lg_page_size;
- tlb_add_large_page(env, mmu_idx, vaddr, sz);
+ tlb_add_large_page(env, mmu_idx, addr, sz);
}
- vaddr_page = vaddr & TARGET_PAGE_MASK;
+ addr_page = addr & TARGET_PAGE_MASK;
paddr_page = full->phys_addr & TARGET_PAGE_MASK;
prot = full->prot;
&xlat, &sz, full->attrs, &prot);
assert(sz >= TARGET_PAGE_SIZE);
- tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" HWADDR_FMT_plx
+ tlb_debug("vaddr=%" VADDR_PRIx " paddr=0x" HWADDR_FMT_plx
" prot=%x idx=%d\n",
- vaddr, full->phys_addr, prot, mmu_idx);
+ addr, full->phys_addr, prot, mmu_idx);
- address = vaddr_page;
+ read_flags = 0;
if (full->lg_page_size < TARGET_PAGE_BITS) {
/* Repeat the MMU check and TLB fill on every access. */
- address |= TLB_INVALID_MASK;
+ read_flags |= TLB_INVALID_MASK;
}
if (full->attrs.byte_swap) {
- address |= TLB_BSWAP;
+ read_flags |= TLB_BSWAP;
}
is_ram = memory_region_is_ram(section->mr);
addend = 0;
}
- write_address = address;
+ write_flags = read_flags;
if (is_ram) {
iotlb = memory_region_get_ram_addr(section->mr) + xlat;
/*
*/
if (prot & PAGE_WRITE) {
if (section->readonly) {
- write_address |= TLB_DISCARD_WRITE;
+ write_flags |= TLB_DISCARD_WRITE;
} else if (cpu_physical_memory_is_clean(iotlb)) {
- write_address |= TLB_NOTDIRTY;
+ write_flags |= TLB_NOTDIRTY;
}
}
} else {
* Reads to romd devices go through the ram_ptr found above,
* but of course reads to I/O must go through MMIO.
*/
- write_address |= TLB_MMIO;
+ write_flags |= TLB_MMIO;
if (!is_romd) {
- address = write_address;
+ read_flags = write_flags;
}
}
- wp_flags = cpu_watchpoint_address_matches(cpu, vaddr_page,
+ wp_flags = cpu_watchpoint_address_matches(cpu, addr_page,
TARGET_PAGE_SIZE);
- index = tlb_index(env, mmu_idx, vaddr_page);
- te = tlb_entry(env, mmu_idx, vaddr_page);
+ index = tlb_index(env, mmu_idx, addr_page);
+ te = tlb_entry(env, mmu_idx, addr_page);
/*
* Hold the TLB lock for the rest of the function. We could acquire/release
tlb->c.dirty |= 1 << mmu_idx;
/* Make sure there's no cached translation for the new page. */
- tlb_flush_vtlb_page_locked(env, mmu_idx, vaddr_page);
+ tlb_flush_vtlb_page_locked(env, mmu_idx, addr_page);
/*
* Only evict the old entry to the victim tlb if it's for a
* different page; otherwise just overwrite the stale data.
*/
- if (!tlb_hit_page_anyprot(te, vaddr_page) && !tlb_entry_is_empty(te)) {
+ if (!tlb_hit_page_anyprot(te, addr_page) && !tlb_entry_is_empty(te)) {
unsigned vidx = desc->vindex++ % CPU_VTLB_SIZE;
CPUTLBEntry *tv = &desc->vtable[vidx];
* TARGET_PAGE_BITS, and either
* + the ram_addr_t of the page base of the target RAM (RAM)
* + the offset within section->mr of the page base (I/O, ROMD)
- * We subtract the vaddr_page (which is page aligned and thus won't
+ * We subtract addr_page (which is page aligned and thus won't
* disturb the low bits) to give an offset which can be added to the
* (non-page-aligned) vaddr of the eventual memory access to get
* the MemoryRegion offset for the access. Note that the vaddr we
* vaddr we add back in io_readx()/io_writex()/get_page_addr_code().
*/
desc->fulltlb[index] = *full;
- desc->fulltlb[index].xlat_section = iotlb - vaddr_page;
- desc->fulltlb[index].phys_addr = paddr_page;
+ full = &desc->fulltlb[index];
+ full->xlat_section = iotlb - addr_page;
+ full->phys_addr = paddr_page;
/* Now calculate the new entry */
- tn.addend = addend - vaddr_page;
- if (prot & PAGE_READ) {
- tn.addr_read = address;
- if (wp_flags & BP_MEM_READ) {
- tn.addr_read |= TLB_WATCHPOINT;
- }
- } else {
- tn.addr_read = -1;
- }
+ tn.addend = addend - addr_page;
- if (prot & PAGE_EXEC) {
- tn.addr_code = address;
- } else {
- tn.addr_code = -1;
+ tlb_set_compare(full, &tn, addr_page, read_flags,
+ MMU_INST_FETCH, prot & PAGE_EXEC);
+
+ if (wp_flags & BP_MEM_READ) {
+ read_flags |= TLB_WATCHPOINT;
}
+ tlb_set_compare(full, &tn, addr_page, read_flags,
+ MMU_DATA_LOAD, prot & PAGE_READ);
- tn.addr_write = -1;
- if (prot & PAGE_WRITE) {
- tn.addr_write = write_address;
- if (prot & PAGE_WRITE_INV) {
- tn.addr_write |= TLB_INVALID_MASK;
- }
- if (wp_flags & BP_MEM_WRITE) {
- tn.addr_write |= TLB_WATCHPOINT;
- }
+ if (prot & PAGE_WRITE_INV) {
+ write_flags |= TLB_INVALID_MASK;
+ }
+ if (wp_flags & BP_MEM_WRITE) {
+ write_flags |= TLB_WATCHPOINT;
}
+ tlb_set_compare(full, &tn, addr_page, write_flags,
+ MMU_DATA_STORE, prot & PAGE_WRITE);
copy_tlb_helper_locked(te, &tn);
tlb_n_used_entries_inc(env, mmu_idx);
qemu_spin_unlock(&tlb->c.lock);
}
-void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
+void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr,
hwaddr paddr, MemTxAttrs attrs, int prot,
- int mmu_idx, target_ulong size)
+ int mmu_idx, uint64_t size)
{
CPUTLBEntryFull full = {
.phys_addr = paddr,
};
assert(is_power_of_2(size));
- tlb_set_page_full(cpu, mmu_idx, vaddr, &full);
+ tlb_set_page_full(cpu, mmu_idx, addr, &full);
}
-void tlb_set_page(CPUState *cpu, target_ulong vaddr,
+void tlb_set_page(CPUState *cpu, vaddr addr,
hwaddr paddr, int prot,
- int mmu_idx, target_ulong size)
+ int mmu_idx, uint64_t size)
{
- tlb_set_page_with_attrs(cpu, vaddr, paddr, MEMTXATTRS_UNSPECIFIED,
+ tlb_set_page_with_attrs(cpu, addr, paddr, MEMTXATTRS_UNSPECIFIED,
prot, mmu_idx, size);
}
* caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must
* be discarded and looked up again (e.g. via tlb_entry()).
*/
-static void tlb_fill(CPUState *cpu, target_ulong addr, int size,
+static void tlb_fill(CPUState *cpu, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
bool ok;
}
static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
- int mmu_idx, target_ulong addr, uintptr_t retaddr,
+ int mmu_idx, vaddr addr, uintptr_t retaddr,
MMUAccessType access_type, MemOp op)
{
CPUState *cpu = env_cpu(env);
}
static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
- int mmu_idx, uint64_t val, target_ulong addr,
+ int mmu_idx, uint64_t val, vaddr addr,
uintptr_t retaddr, MemOp op)
{
CPUState *cpu = env_cpu(env);
/* Return true if ADDR is present in the victim tlb, and has been copied
back to the main tlb. */
static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
- MMUAccessType access_type, target_ulong page)
+ MMUAccessType access_type, vaddr page)
{
size_t vidx;
assert_cpu_is_self(env_cpu(env));
for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) {
CPUTLBEntry *vtlb = &env_tlb(env)->d[mmu_idx].vtable[vidx];
- target_ulong cmp = tlb_read_idx(vtlb, access_type);
+ uint64_t cmp = tlb_read_idx(vtlb, access_type);
if (cmp == page) {
/* Found entry in victim tlb, swap tlb and iotlb. */
}
}
-static int probe_access_internal(CPUArchState *env, target_ulong addr,
+static int probe_access_internal(CPUArchState *env, vaddr addr,
int fault_size, MMUAccessType access_type,
int mmu_idx, bool nonfault,
void **phost, CPUTLBEntryFull **pfull,
{
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
- target_ulong tlb_addr = tlb_read_idx(entry, access_type);
- target_ulong page_addr = addr & TARGET_PAGE_MASK;
- int flags = TLB_FLAGS_MASK;
+ uint64_t tlb_addr = tlb_read_idx(entry, access_type);
+ vaddr page_addr = addr & TARGET_PAGE_MASK;
+ int flags = TLB_FLAGS_MASK & ~TLB_FORCE_SLOW;
+ CPUTLBEntryFull *full;
if (!tlb_hit_page(tlb_addr, page_addr)) {
if (!victim_tlb_hit(env, mmu_idx, index, access_type, page_addr)) {
}
flags &= tlb_addr;
- *pfull = &env_tlb(env)->d[mmu_idx].fulltlb[index];
+ *pfull = full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
+ flags |= full->slow_flags[access_type];
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) {
return flags;
}
-int probe_access_full(CPUArchState *env, target_ulong addr, int size,
+int probe_access_full(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, CPUTLBEntryFull **pfull,
uintptr_t retaddr)
return flags;
}
-int probe_access_flags(CPUArchState *env, target_ulong addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr)
{
return flags;
}
-void *probe_access(CPUArchState *env, target_ulong addr, int size,
+void *probe_access(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
CPUTLBEntryFull *full;
* NOTE: This function will trigger an exception if the page is
* not executable.
*/
-tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
+tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
void **hostp)
{
CPUTLBEntryFull *full;
* from the same thread (which a mem callback will be) this is safe.
*/
-bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx,
+bool tlb_plugin_lookup(CPUState *cpu, vaddr addr, int mmu_idx,
bool is_store, struct qemu_plugin_hwaddr *data)
{
CPUArchState *env = cpu->env_ptr;
CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr);
uintptr_t index = tlb_index(env, mmu_idx, addr);
- target_ulong tlb_addr = is_store ? tlb_addr_write(tlbe) : tlbe->addr_read;
+ uint64_t tlb_addr = is_store ? tlb_addr_write(tlbe) : tlbe->addr_read;
if (likely(tlb_hit(tlb_addr, addr))) {
/* We must have an iotlb entry for MMIO */
typedef struct MMULookupPageData {
CPUTLBEntryFull *full;
void *haddr;
- target_ulong addr;
+ vaddr addr;
int flags;
int size;
} MMULookupPageData;
static bool mmu_lookup1(CPUArchState *env, MMULookupPageData *data,
int mmu_idx, MMUAccessType access_type, uintptr_t ra)
{
- target_ulong addr = data->addr;
+ vaddr addr = data->addr;
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
- target_ulong tlb_addr = tlb_read_idx(entry, access_type);
+ uint64_t tlb_addr = tlb_read_idx(entry, access_type);
bool maybe_resized = false;
+ CPUTLBEntryFull *full;
+ int flags;
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
tlb_addr = tlb_read_idx(entry, access_type) & ~TLB_INVALID_MASK;
}
- data->flags = tlb_addr & TLB_FLAGS_MASK;
- data->full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
+ full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
+ flags = tlb_addr & (TLB_FLAGS_MASK & ~TLB_FORCE_SLOW);
+ flags |= full->slow_flags[access_type];
+
+ data->full = full;
+ data->flags = flags;
/* Compute haddr speculatively; depending on flags it might be invalid. */
data->haddr = (void *)((uintptr_t)addr + entry->addend);
MMUAccessType access_type, uintptr_t ra)
{
CPUTLBEntryFull *full = data->full;
- target_ulong addr = data->addr;
+ vaddr addr = data->addr;
int flags = data->flags;
int size = data->size;
* Resolve the translation for the page(s) beginning at @addr, for MemOp.size
* bytes. Return true if the lookup crosses a page boundary.
*/
-static bool mmu_lookup(CPUArchState *env, target_ulong addr, MemOpIdx oi,
+static bool mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType type, MMULookupLocals *l)
{
unsigned a_bits;
* Probe for an atomic operation. Do not allow unaligned operations,
* or io operations to proceed. Return the host address.
*/
-static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
- MemOpIdx oi, int size, uintptr_t retaddr)
+static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi,
+ int size, uintptr_t retaddr)
{
uintptr_t mmu_idx = get_mmuidx(oi);
MemOp mop = get_memop(oi);
int a_bits = get_alignment_bits(mop);
uintptr_t index;
CPUTLBEntry *tlbe;
- target_ulong tlb_addr;
+ vaddr tlb_addr;
void *hostaddr;
CPUTLBEntryFull *full;
*/
goto stop_the_world;
}
- /* Collect TLB_WATCHPOINT for read. */
+ /* Collect tlb flags for read. */
tlb_addr |= tlbe->addr_read;
/* Notice an IO access or a needs-MMU-lookup access */
notdirty_write(env_cpu(env), addr, size, full, retaddr);
}
- if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
- cpu_check_watchpoint(env_cpu(env), addr, size, full->attrs,
- BP_MEM_READ | BP_MEM_WRITE, retaddr);
+ if (unlikely(tlb_addr & TLB_FORCE_SLOW)) {
+ int wp_flags = 0;
+
+ if (full->slow_flags[MMU_DATA_STORE] & TLB_WATCHPOINT) {
+ wp_flags |= BP_MEM_WRITE;
+ }
+ if (full->slow_flags[MMU_DATA_LOAD] & TLB_WATCHPOINT) {
+ wp_flags |= BP_MEM_READ;
+ }
+ if (wp_flags) {
+ cpu_check_watchpoint(env_cpu(env), addr, size,
+ full->attrs, wp_flags, retaddr);
+ }
}
return hostaddr;
MMUAccessType type, uintptr_t ra)
{
CPUTLBEntryFull *full = p->full;
- target_ulong addr = p->addr;
+ vaddr addr = p->addr;
int i, size = p->size;
QEMU_IOTHREAD_LOCK_GUARD();
return ret;
}
-static uint8_t do_ld1_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi,
+static uint8_t do_ld1_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType access_type)
{
MMULookupLocals l;
bool crosspage;
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l);
tcg_debug_assert(!crosspage);
return do_ld1_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
}
-static uint16_t do_ld2_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi,
+static uint16_t do_ld2_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType access_type)
{
MMULookupLocals l;
uint16_t ret;
uint8_t a, b;
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l);
if (likely(!crosspage)) {
return do_ld_2(env, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
return do_ld2_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
}
-static uint32_t do_ld4_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi,
+static uint32_t do_ld4_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType access_type)
{
MMULookupLocals l;
bool crosspage;
uint32_t ret;
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l);
if (likely(!crosspage)) {
return do_ld_4(env, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
return do_ld4_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
}
-static uint64_t do_ld8_mmu(CPUArchState *env, target_ulong addr, MemOpIdx oi,
+static uint64_t do_ld8_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType access_type)
{
MMULookupLocals l;
bool crosspage;
uint64_t ret;
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l);
if (likely(!crosspage)) {
return do_ld_8(env, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
return (int32_t)helper_ldul_mmu(env, addr, oi, retaddr);
}
-static Int128 do_ld16_mmu(CPUArchState *env, target_ulong addr,
+static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr,
MemOpIdx oi, uintptr_t ra)
{
MMULookupLocals l;
Int128 ret;
int first;
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD, &l);
if (likely(!crosspage)) {
/* Perform the load host endian. */
uint64_t val_le, int mmu_idx, uintptr_t ra)
{
CPUTLBEntryFull *full = p->full;
- target_ulong addr = p->addr;
+ vaddr addr = p->addr;
int i, size = p->size;
QEMU_IOTHREAD_LOCK_GUARD();
bool crosspage;
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8);
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l);
tcg_debug_assert(!crosspage);
do_st_1(env, &l.page[0], val, l.mmu_idx, ra);
}
-static void do_st2_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
+static void do_st2_mmu(CPUArchState *env, vaddr addr, uint16_t val,
MemOpIdx oi, uintptr_t ra)
{
MMULookupLocals l;
bool crosspage;
uint8_t a, b;
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
do_st_2(env, &l.page[0], val, l.mmu_idx, l.memop, ra);
do_st2_mmu(env, addr, val, oi, retaddr);
}
-static void do_st4_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
+static void do_st4_mmu(CPUArchState *env, vaddr addr, uint32_t val,
MemOpIdx oi, uintptr_t ra)
{
MMULookupLocals l;
bool crosspage;
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
do_st_4(env, &l.page[0], val, l.mmu_idx, l.memop, ra);
do_st4_mmu(env, addr, val, oi, retaddr);
}
-static void do_st8_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
+static void do_st8_mmu(CPUArchState *env, vaddr addr, uint64_t val,
MemOpIdx oi, uintptr_t ra)
{
MMULookupLocals l;
bool crosspage;
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
do_st_8(env, &l.page[0], val, l.mmu_idx, l.memop, ra);
do_st8_mmu(env, addr, val, oi, retaddr);
}
-static void do_st16_mmu(CPUArchState *env, target_ulong addr, Int128 val,
+static void do_st16_mmu(CPUArchState *env, vaddr addr, Int128 val,
MemOpIdx oi, uintptr_t ra)
{
MMULookupLocals l;
uint64_t a, b;
int first;
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
/* Swap to host endian if necessary, then store. */
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
#endif /* CONFIG_SOFTMMU */
-TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc,
- target_ulong cs_base, uint32_t flags,
+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);
uintptr_t host_pc);
/* Return the current PC from CPU, which may be cached in TB. */
-static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb)
+static inline vaddr log_pc(CPUState *cpu, const TranslationBlock *tb)
{
if (tb_cflags(tb) & CF_PCREL) {
return cpu->cc->get_pc(cpu);
extern bool one_insn_per_tb;
+/**
+ * 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 */
return human_readable_text_from_str(buf);
}
-#ifdef CONFIG_PROFILER
-
-int64_t dev_time;
-
-HumanReadableText *qmp_x_query_profile(Error **errp)
-{
- g_autoptr(GString) buf = g_string_new("");
- static int64_t last_cpu_exec_time;
- int64_t cpu_exec_time;
- int64_t delta;
-
- cpu_exec_time = tcg_cpu_exec_time();
- delta = cpu_exec_time - last_cpu_exec_time;
-
- g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n",
- dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
- g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n",
- delta, delta / (double)NANOSECONDS_PER_SECOND);
- last_cpu_exec_time = cpu_exec_time;
- dev_time = 0;
-
- return human_readable_text_from_str(buf);
-}
-#else
-HumanReadableText *qmp_x_query_profile(Error **errp)
-{
- error_setg(errp, "Internal profiler not compiled");
- return NULL;
-}
-#endif
-
static void hmp_tcg_register(void)
{
monitor_register_hmp_info_hrt("jit", qmp_x_query_jit);
#define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1)
#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
-static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
+static inline unsigned int tb_jmp_cache_hash_page(vaddr pc)
{
- target_ulong tmp;
+ vaddr tmp;
tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
return (tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK;
}
-static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
+static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
{
- target_ulong tmp;
+ vaddr tmp;
tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
return (((tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK)
| (tmp & TB_JMP_ADDR_MASK));
#else
/* In user-mode we can get better hashing because we do not have a TLB */
-static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
+static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
{
return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_SIZE - 1);
}
#endif /* CONFIG_SOFTMMU */
static inline
-uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc,
+uint32_t tb_hash_func(tb_page_addr_t phys_pc, vaddr pc,
uint32_t flags, uint64_t flags2, uint32_t cf_mask)
{
return qemu_xxhash8(phys_pc, pc, flags2, flags, cf_mask);
struct rcu_head rcu;
struct {
TranslationBlock *tb;
- target_ulong pc;
+ vaddr pc;
} array[TB_JMP_CACHE_SIZE];
};
/* Call with mmap_lock held. */
static void tb_record(TranslationBlock *tb, PageDesc *p1, PageDesc *p2)
{
- target_ulong addr;
+ vaddr addr;
int flags;
assert_memory_lock();
int tcg_cpus_exec(CPUState *cpu)
{
int ret;
-#ifdef CONFIG_PROFILER
- int64_t ti;
-#endif
assert(tcg_enabled());
-#ifdef CONFIG_PROFILER
- ti = profile_getclock();
-#endif
cpu_exec_start(cpu);
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
-#ifdef CONFIG_PROFILER
- qatomic_set(&tcg_ctx->prof.cpu_exec_time,
- tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
-#endif
return ret;
}
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
*
* Once a guest architecture has been converted to the new primitives
- * there are two remaining limitations to check.
- *
- * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
- * - The host must have a stronger memory order than the guest
- *
- * It may be possible in future to support strong guests on weak hosts
- * but that will require tagging all load/stores in a guest with their
- * implicit memory order requirements which would likely slow things
- * down a lot.
+ * there is one remaining limitation to check:
+ * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
*/
-static bool check_tcg_memory_orders_compatible(void)
-{
-#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO)
- return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0;
-#else
- return false;
-#endif
-}
-
static bool default_mttcg_enabled(void)
{
if (icount_enabled() || TCG_OVERSIZED_GUEST) {
return false;
- } else {
+ }
#ifdef TARGET_SUPPORTS_MTTCG
- return check_tcg_memory_orders_compatible();
+# ifndef TCG_GUEST_DEFAULT_MO
+# error "TARGET_SUPPORTS_MTTCG without TCG_GUEST_DEFAULT_MO"
+# endif
+ return true;
#else
- return false;
+ return false;
#endif
- }
}
static void tcg_accel_instance_init(Object *obj)
warn_report("Guest not yet converted to MTTCG - "
"you may get unexpected results");
#endif
- if (!check_tcg_memory_orders_compatible()) {
- warn_report("Guest expects a stronger memory ordering "
- "than the host provides");
- error_printf("This may cause strange/hard to debug errors\n");
- }
s->mttcg_enabled = true;
}
} else if (strcmp(value, "single") == 0) {
uintptr_t host_pc)
{
uint64_t data[TARGET_INSN_START_WORDS];
-#ifdef CONFIG_PROFILER
- TCGProfile *prof = &tcg_ctx->prof;
- int64_t ti = profile_getclock();
-#endif
int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data);
if (insns_left < 0) {
}
cpu->cc->tcg_ops->restore_state_to_opc(cpu, tb, data);
-
-#ifdef CONFIG_PROFILER
- qatomic_set(&prof->restore_time,
- prof->restore_time + profile_getclock() - ti);
- qatomic_set(&prof->restore_count, prof->restore_count + 1);
-#endif
}
bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc)
* Return the size of the generated code, or negative on error.
*/
static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
- target_ulong pc, void *host_pc,
+ vaddr pc, void *host_pc,
int *max_insns, int64_t *ti)
{
int ret = sigsetjmp(tcg_ctx->jmp_trans, 0);
tcg_ctx->cpu = NULL;
*max_insns = tb->icount;
-#ifdef CONFIG_PROFILER
- qatomic_set(&tcg_ctx->prof.tb_count, tcg_ctx->prof.tb_count + 1);
- qatomic_set(&tcg_ctx->prof.interm_time,
- tcg_ctx->prof.interm_time + profile_getclock() - *ti);
- *ti = profile_getclock();
-#endif
-
return tcg_gen_code(tcg_ctx, tb, pc);
}
/* Called with mmap_lock held for user mode emulation. */
TranslationBlock *tb_gen_code(CPUState *cpu,
- target_ulong pc, target_ulong cs_base,
+ vaddr pc, uint64_t cs_base,
uint32_t flags, int cflags)
{
CPUArchState *env = cpu->env_ptr;
tb_page_addr_t phys_pc;
tcg_insn_unit *gen_code_buf;
int gen_code_size, search_size, max_insns;
-#ifdef CONFIG_PROFILER
- TCGProfile *prof = &tcg_ctx->prof;
-#endif
int64_t ti;
void *host_pc;
tb_overflow:
-#ifdef CONFIG_PROFILER
- /* includes aborted translations because of exceptions */
- qatomic_set(&prof->tb_count1, prof->tb_count1 + 1);
- ti = profile_getclock();
-#endif
-
trace_translate_block(tb, pc, tb->tc.ptr);
gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti);
*/
perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf));
-#ifdef CONFIG_PROFILER
- qatomic_set(&prof->code_time, prof->code_time + profile_getclock() - ti);
- qatomic_set(&prof->code_in_len, prof->code_in_len + tb->size);
- qatomic_set(&prof->code_out_len, prof->code_out_len + gen_code_size);
- qatomic_set(&prof->search_out_len, prof->search_out_len + search_size);
-#endif
-
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
qemu_log_in_addr_range(pc)) {
FILE *logfile = qemu_log_trylock();
/* 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_ptr;
- target_ulong pc, cs_base;
+ vaddr pc;
+ uint64_t cs_base;
tb_page_addr_t addr;
uint32_t flags;
cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_LAST_IO | n;
if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
- target_ulong pc = log_pc(cpu, tb);
+ vaddr pc = log_pc(cpu, tb);
if (qemu_log_in_addr_range(pc)) {
- qemu_log("cpu_io_recompile: rewound execution of TB to "
- TARGET_FMT_lx "\n", pc);
+ qemu_log("cpu_io_recompile: rewound execution of TB to %"
+ VADDR_PRIx "\n", pc);
}
}
}
}
-bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
+bool translator_use_goto_tb(DisasContextBase *db, vaddr dest)
{
/* Suppress goto_tb if requested. */
if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
}
void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc,
- const TranslatorOps *ops, DisasContextBase *db)
+ vaddr pc, void *host_pc, const TranslatorOps *ops,
+ DisasContextBase *db)
{
uint32_t cflags = tb_cflags(tb);
TCGOp *icount_start_insn;
}
static void *translator_access(CPUArchState *env, DisasContextBase *db,
- target_ulong pc, size_t len)
+ vaddr pc, size_t len)
{
void *host;
- target_ulong base, end;
+ vaddr base, end;
TranslationBlock *tb;
tb = db->tb;
return current_tb_invalidated ? 2 : 1;
}
-static int probe_access_internal(CPUArchState *env, target_ulong addr,
+static int probe_access_internal(CPUArchState *env, vaddr addr,
int fault_size, MMUAccessType access_type,
bool nonfault, uintptr_t ra)
{
cpu_loop_exit_sigsegv(env_cpu(env), addr, access_type, maperr, ra);
}
-int probe_access_flags(CPUArchState *env, target_ulong addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t ra)
{
return flags;
}
-void *probe_access(CPUArchState *env, target_ulong addr, int size,
+void *probe_access(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t ra)
{
int flags;
return size ? g2h(env_cpu(env), addr) : NULL;
}
-tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
+tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
void **hostp)
{
int flags;
/* The softmmu versions of these helpers are in cputlb.c. */
-static void *cpu_mmu_lookup(CPUArchState *env, abi_ptr addr,
+static void *cpu_mmu_lookup(CPUArchState *env, vaddr addr,
MemOp mop, uintptr_t ra, MMUAccessType type)
{
int a_bits = get_alignment_bits(mop);
uint8_t ret;
tcg_debug_assert((mop & MO_SIZE) == MO_8);
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD);
ret = ldub_p(haddr);
clear_helper_retaddr();
uint16_t ret;
tcg_debug_assert((mop & MO_SIZE) == MO_16);
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD);
ret = load_atom_2(env, ra, haddr, mop);
clear_helper_retaddr();
uint32_t ret;
tcg_debug_assert((mop & MO_SIZE) == MO_32);
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD);
ret = load_atom_4(env, ra, haddr, mop);
clear_helper_retaddr();
uint64_t ret;
tcg_debug_assert((mop & MO_SIZE) == MO_64);
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD);
ret = load_atom_8(env, ra, haddr, mop);
clear_helper_retaddr();
Int128 ret;
tcg_debug_assert((mop & MO_SIZE) == MO_128);
+ cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD);
ret = load_atom_16(env, ra, haddr, mop);
clear_helper_retaddr();
void *haddr;
tcg_debug_assert((mop & MO_SIZE) == MO_8);
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE);
stb_p(haddr, val);
clear_helper_retaddr();
void *haddr;
tcg_debug_assert((mop & MO_SIZE) == MO_16);
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
void *haddr;
tcg_debug_assert((mop & MO_SIZE) == MO_32);
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
void *haddr;
tcg_debug_assert((mop & MO_SIZE) == MO_64);
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
void *haddr;
tcg_debug_assert((mop & MO_SIZE) == MO_128);
+ cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
/*
* Do not allow unaligned operations to proceed. Return the host address.
*/
-static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
- MemOpIdx oi, int size, uintptr_t retaddr)
+static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi,
+ int size, uintptr_t retaddr)
{
MemOp mop = get_memop(oi);
int a_bits = get_alignment_bits(mop);
#include "qemu/timer.h"
#include "qemu/dbus.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
+
+#include "ui/dbus.h"
#include "ui/dbus-display1.h"
#define AUDIO_CAP "dbus"
static gboolean
dbus_audio_register_listener(AudioState *s,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener,
bool out)
{
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifdef G_OS_WIN32
+ if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+#else
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
if (err) {
g_dbus_method_invocation_return_error(invocation,
err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
socket = g_socket_new_from_fd(fd, &err);
if (err) {
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't make a socket: %s",
err->message);
+#ifdef G_OS_WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
return DBUS_METHOD_INVOCATION_HANDLED;
}
socket_conn = g_socket_connection_factory_create_connection(socket);
if (out) {
qemu_dbus_display1_audio_complete_register_out_listener(
- da->iface, invocation, NULL);
+ da->iface, invocation
+#ifdef G_OS_UNIX
+ , NULL
+#endif
+ );
} else {
qemu_dbus_display1_audio_complete_register_in_listener(
- da->iface, invocation, NULL);
+ da->iface, invocation
+#ifdef G_OS_UNIX
+ , NULL
+#endif
+ );
}
listener_conn =
static gboolean
dbus_audio_register_out_listener(AudioState *s,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener)
{
return dbus_audio_register_listener(s, invocation,
- fd_list, arg_listener, true);
+#ifdef G_OS_UNIX
+ fd_list,
+#endif
+ arg_listener, true);
}
static gboolean
dbus_audio_register_in_listener(AudioState *s,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener)
{
return dbus_audio_register_listener(s, invocation,
- fd_list, arg_listener, false);
+#ifdef G_OS_UNIX
+ fd_list,
+#endif
+ arg_listener, false);
}
static void
}
break;
+ case EXCP_SYSCALL:
+ /* doesn't do anything */
+ break;
+
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
}
}
- dwMode |= ENABLE_LINE_INPUT;
+ dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT;
if (is_console) {
/* set the terminal in raw mode */
if ! test -e "$source_path/.git"
then
- git_submodules_action="ignore"
+ git_submodules_action="validate"
fi
# test for any invalid configuration combinations
if test "$?" -ne 0 ; then
error_exit "meson setup failed"
fi
+ echo "$meson" > build.ninja.stamp
else
if test -f meson-private/cmd_line.txt; then
# Adjust old command line options that were removed
}
#if defined(CONFIG_USER_ONLY)
-void tb_invalidate_phys_addr(target_ulong addr)
+void tb_invalidate_phys_addr(hwaddr addr)
{
mmap_lock();
tb_invalidate_phys_page(addr);
repository CI settings, or as git push variables, to influence
which jobs get run in a pipeline
+ * QEMU_CI_CONTAINER_TAG - the tag used to publish containers
+ in stage 1, for use by build jobs in stage 2. Defaults to
+ 'latest', but if running pipelines for different branches
+ concurrently, it should be overridden per pipeline.
+
+ * QEMU_CI_UPSTREAM - gitlab namespace that is considered to be
+ the 'upstream'. This defaults to 'qemu-project'. Contributors
+ may choose to override this if they are modifying rules in
+ base.yml and need to validate how they will operate when in
+ an upstream context, as opposed to their fork context.
+
* nnn - other misc variables not falling into the above
categories, or using different names for historical reasons
and not yet converted.
than the maximum vector length enabled, the actual vector length will
be reduced. If this property is set to ``-1`` then the default vector
length is set to the maximum possible length.
+
+RME CPU Properties
+==================
+
+The status of RME support with QEMU is experimental. At this time we
+only support RME within the CPU proper, not within the SMMU or GIC.
+The feature is enabled by the CPU property ``x-rme``, with the ``x-``
+prefix present as a reminder of the experimental status, and defaults off.
+
+The method for enabling RME will change in some future QEMU release
+without notice or backward compatibility.
+
+RME Level 0 GPT Size Property
+-----------------------------
+
+To aid firmware developers in testing different possible CPU
+configurations, ``x-l0gptsz=S`` may be used to specify the value
+to encode into ``GPCCR_EL3.L0GPTSZ``, a read-only field that
+specifies the size of the Level 0 Granule Protection Table.
+Legal values for ``S`` are 30, 34, 36, and 39; the default is 30.
+
+As with ``x-rme``, the ``x-l0gptsz`` property may be renamed or
+removed in some future QEMU release.
- FEAT_RAS (Reliability, availability, and serviceability)
- FEAT_RASv1p1 (RAS Extension v1.1)
- FEAT_RDM (Advanced SIMD rounding double multiply accumulate instructions)
+- FEAT_RME (Realm Management Extension) (NB: support status in QEMU is experimental)
- FEAT_RNG (Random number generator)
- FEAT_S2FWB (Stage 2 forced Write-Back)
- FEAT_SB (Speculation Barrier)
- platform version
- GIC addresses
+Platform version
+''''''''''''''''
+
The platform version is only for informing platform firmware about
what kind of ``sbsa-ref`` board it is running on. It is neither
a QEMU versioned machine type nor a reflection of the level of the
The ``machine-version-major`` value is updated when changes breaking
fw compatibility are introduced. The ``machine-version-minor`` value
is updated when features are added that don't break fw compatibility.
+
+Platform version changes:
+
+0.0
+ Devicetree holds information about CPUs, memory and platform version.
+
+0.1
+ GIC information is present in devicetree.
+
+0.2
+ GIC ITS information is present in devicetree.
the minimum memory page size (CAP.MPSMIN). The default value (``0``)
has this property inherit the ``mdts`` value.
+Flexible Data Placement
+-----------------------
+
+The device may be configured to support TP4146 ("Flexible Data Placement") by
+configuring it (``fdp=on``) on the subsystem::
+
+ -device nvme-subsys,id=nvme-subsys-0,nqn=subsys0,fdp=on,fdp.nruh=16
+
+The subsystem emulates a single Endurance Group, on which Flexible Data
+Placement will be supported. Also note that the device emulation deviates
+slightly from the specification, by always enabling the "FDP Mode" feature on
+the controller if the subsystems is configured for Flexible Data Placement.
+
+Enabling Flexible Data Placement on the subsyste enables the following
+parameters:
+
+``fdp.nrg`` (default: ``1``)
+ Set the number of Reclaim Groups.
+
+``fdp.nruh`` (default: ``0``)
+ Set the number of Reclaim Unit Handles. This is a mandatory paramater and
+ must be non-zero.
+
+``fdp.runs`` (default: ``96M``)
+ Set the Reclaim Unit Nominal Size. Defaults to 96 MiB.
+
+Namespaces within this subsystem may requests Reclaim Unit Handles::
+
+ -device nvme-ns,drive=nvm-1,fdp.ruhs=RUHLIST
+
+The ``RUHLIST`` is a semicolon separated list (i.e. ``0;1;2;3``) and may
+include ranges (i.e. ``0;8-15``). If no reclaim unit handle list is specified,
+the controller will assign the controller-specified reclaim unit handle to
+placement handle identifier 0.
+
Metadata
--------
.. code-block:: console
- echo 0000:01:00.1 > /sys/bus/pci/drivers/nvme/bind
\ No newline at end of file
+ echo 0000:01:00.1 > /sys/bus/pci/drivers/nvme/bind
};
}
-static inline void float16_unpack_raw(FloatParts64 *p, float16 f)
+static void QEMU_FLATTEN float16_unpack_raw(FloatParts64 *p, float16 f)
{
unpack_raw64(p, &float16_params, f);
}
-static inline void bfloat16_unpack_raw(FloatParts64 *p, bfloat16 f)
+static void QEMU_FLATTEN bfloat16_unpack_raw(FloatParts64 *p, bfloat16 f)
{
unpack_raw64(p, &bfloat16_params, f);
}
-static inline void float32_unpack_raw(FloatParts64 *p, float32 f)
+static void QEMU_FLATTEN float32_unpack_raw(FloatParts64 *p, float32 f)
{
unpack_raw64(p, &float32_params, f);
}
-static inline void float64_unpack_raw(FloatParts64 *p, float64 f)
+static void QEMU_FLATTEN float64_unpack_raw(FloatParts64 *p, float64 f)
{
unpack_raw64(p, &float64_params, f);
}
-static void floatx80_unpack_raw(FloatParts128 *p, floatx80 f)
+static void QEMU_FLATTEN floatx80_unpack_raw(FloatParts128 *p, floatx80 f)
{
*p = (FloatParts128) {
.cls = float_class_unclassified,
};
}
-static void float128_unpack_raw(FloatParts128 *p, float128 f)
+static void QEMU_FLATTEN float128_unpack_raw(FloatParts128 *p, float128 f)
{
const int f_size = float128_params.frac_size - 64;
const int e_size = float128_params.exp_size;
return ret;
}
-static inline float16 float16_pack_raw(const FloatParts64 *p)
+static float16 QEMU_FLATTEN float16_pack_raw(const FloatParts64 *p)
{
return make_float16(pack_raw64(p, &float16_params));
}
-static inline bfloat16 bfloat16_pack_raw(const FloatParts64 *p)
+static bfloat16 QEMU_FLATTEN bfloat16_pack_raw(const FloatParts64 *p)
{
return pack_raw64(p, &bfloat16_params);
}
-static inline float32 float32_pack_raw(const FloatParts64 *p)
+static float32 QEMU_FLATTEN float32_pack_raw(const FloatParts64 *p)
{
return make_float32(pack_raw64(p, &float32_params));
}
-static inline float64 float64_pack_raw(const FloatParts64 *p)
+static float64 QEMU_FLATTEN float64_pack_raw(const FloatParts64 *p)
{
return make_float64(pack_raw64(p, &float64_params));
}
-static float128 float128_pack_raw(const FloatParts128 *p)
+static float128 QEMU_FLATTEN float128_pack_raw(const FloatParts128 *p)
{
const int f_size = float128_params.frac_size - 64;
const int e_size = float128_params.exp_size;
Show host USB devices.
ERST
-#if defined(CONFIG_TCG)
- {
- .name = "profile",
- .args_type = "",
- .params = "",
- .help = "show profiling information",
- .cmd_info_hrt = qmp_x_query_profile,
- },
-#endif
-
-SRST
- ``info profile``
- Show profiling information.
-ERST
-
{
.name = "capture",
.args_type = "",
SBSA_CPUPERIPHS,
SBSA_GIC_DIST,
SBSA_GIC_REDIST,
+ SBSA_GIC_ITS,
SBSA_SECURE_EC,
SBSA_GWDT_WS0,
SBSA_GWDT_REFRESH,
[SBSA_CPUPERIPHS] = { 0x40000000, 0x00040000 },
[SBSA_GIC_DIST] = { 0x40060000, 0x00010000 },
[SBSA_GIC_REDIST] = { 0x40080000, 0x04000000 },
+ [SBSA_GIC_ITS] = { 0x44081000, 0x00020000 },
[SBSA_SECURE_EC] = { 0x50000000, 0x00001000 },
[SBSA_GWDT_REFRESH] = { 0x50010000, 0x00001000 },
[SBSA_GWDT_CONTROL] = { 0x50011000, 0x00001000 },
2, sbsa_ref_memmap[SBSA_GIC_REDIST].base,
2, sbsa_ref_memmap[SBSA_GIC_REDIST].size);
+ nodename = g_strdup_printf("/intc/its");
+ qemu_fdt_add_subnode(sms->fdt, nodename);
+ qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg",
+ 2, sbsa_ref_memmap[SBSA_GIC_ITS].base,
+ 2, sbsa_ref_memmap[SBSA_GIC_ITS].size);
+
g_free(nodename);
}
+
/*
* Firmware on this machine only uses ACPI table to load OS, these limited
* device tree nodes are just to let firmware know the info which varies from
* fw compatibility.
*/
qemu_fdt_setprop_cell(fdt, "/", "machine-version-major", 0);
- qemu_fdt_setprop_cell(fdt, "/", "machine-version-minor", 1);
+ qemu_fdt_setprop_cell(fdt, "/", "machine-version-minor", 2);
if (ms->numa_state->have_numa_distance) {
int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t);
memory_region_add_subregion(secure_sysmem, base, secram);
}
-static void create_gic(SBSAMachineState *sms)
+static void create_its(SBSAMachineState *sms)
+{
+ const char *itsclass = its_class_name();
+ DeviceState *dev;
+
+ dev = qdev_new(itsclass);
+
+ object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(sms->gic),
+ &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, sbsa_ref_memmap[SBSA_GIC_ITS].base);
+}
+
+static void create_gic(SBSAMachineState *sms, MemoryRegion *mem)
{
unsigned int smp_cpus = MACHINE(sms)->smp.cpus;
SysBusDevice *gicbusdev;
qdev_prop_set_uint32(sms->gic, "len-redist-region-count", 1);
qdev_prop_set_uint32(sms->gic, "redist-region-count[0]", redist0_count);
+ object_property_set_link(OBJECT(sms->gic), "sysmem",
+ OBJECT(mem), &error_fatal);
+ qdev_prop_set_bit(sms->gic, "has-lpi", true);
+
gicbusdev = SYS_BUS_DEVICE(sms->gic);
sysbus_realize_and_unref(gicbusdev, &error_fatal);
sysbus_mmio_map(gicbusdev, 0, sbsa_ref_memmap[SBSA_GIC_DIST].base);
sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus,
qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
}
+ create_its(sms);
}
static void create_uart(const SBSAMachineState *sms, int uart,
create_secure_ram(sms, secure_sysmem);
- create_gic(sms);
+ create_gic(sms, sysmem);
create_uart(sms, SBSA_UART, sysmem, serial_hd(0));
create_uart(sms, SBSA_SECURE_UART, secure_sysmem, serial_hd(1));
mc->possible_cpu_arch_ids = sbsa_ref_possible_cpu_arch_ids;
mc->cpu_index_to_instance_props = sbsa_ref_cpu_index_to_props;
mc->get_default_cpu_node_id = sbsa_ref_get_default_cpu_node_id;
+ /* platform instead of architectural choice */
+ mc->cpu_cluster_has_numa_boundary = true;
}
static const TypeInfo sbsa_ref_info = {
mc->smp_props.clusters_supported = true;
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
+ /* platform instead of architectural choice */
+ mc->cpu_cluster_has_numa_boundary = true;
mc->default_ram_id = "mach-virt.ram";
mc->default_nic = "virtio-net-pci";
g_string_free(s, true);
}
+static void validate_cpu_cluster_to_numa_boundary(MachineState *ms)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ NumaState *state = ms->numa_state;
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+ const CPUArchId *cpus = possible_cpus->cpus;
+ int i, j;
+
+ if (state->num_nodes <= 1 || possible_cpus->len <= 1) {
+ return;
+ }
+
+ /*
+ * The Linux scheduling domain can't be parsed when the multiple CPUs
+ * in one cluster have been associated with different NUMA nodes. However,
+ * it's fine to associate one NUMA node with CPUs in different clusters.
+ */
+ for (i = 0; i < possible_cpus->len; i++) {
+ for (j = i + 1; j < possible_cpus->len; j++) {
+ if (cpus[i].props.has_socket_id &&
+ cpus[i].props.has_cluster_id &&
+ cpus[i].props.has_node_id &&
+ cpus[j].props.has_socket_id &&
+ cpus[j].props.has_cluster_id &&
+ cpus[j].props.has_node_id &&
+ cpus[i].props.socket_id == cpus[j].props.socket_id &&
+ cpus[i].props.cluster_id == cpus[j].props.cluster_id &&
+ cpus[i].props.node_id != cpus[j].props.node_id) {
+ warn_report("CPU-%d and CPU-%d in socket-%" PRId64 "-cluster-%" PRId64
+ " have been associated with node-%" PRId64 " and node-%" PRId64
+ " respectively. It can cause OSes like Linux to"
+ " misbehave", i, j, cpus[i].props.socket_id,
+ cpus[i].props.cluster_id, cpus[i].props.node_id,
+ cpus[j].props.node_id);
+ }
+ }
+ }
+}
+
MemoryRegion *machine_consume_memdev(MachineState *machine,
HostMemoryBackend *backend)
{
numa_complete_configuration(machine);
if (machine->numa_state->num_nodes) {
machine_numa_finish_cpu_init(machine);
+ if (machine_class->cpu_cluster_has_numa_boundary) {
+ validate_cpu_cluster_to_numa_boundary(machine);
+ }
}
}
void *pdata = NULL;
res->dmabuf_fd = -1;
- if (res->iov_cnt == 1) {
+ if (res->iov_cnt == 1 &&
+ res->iov[0].iov_len < 4096) {
pdata = res->iov[0].iov_base;
} else {
virtio_gpu_create_udmabuf(res);
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-gpu.h"
+#include "ui/egl-helpers.h"
+
#include <virglrenderer.h>
-static struct virgl_renderer_callbacks virtio_gpu_3d_cbs;
+#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
+static void *
+virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
+{
+ return qemu_egl_display;
+}
+#endif
static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_set_scanout ss;
- struct virgl_renderer_resource_info info;
int ret;
VIRTIO_GPU_FILL_CMD(ss);
}
g->parent_obj.enable = 1;
- memset(&info, 0, sizeof(info));
-
if (ss.resource_id && ss.r.width && ss.r.height) {
+ struct virgl_renderer_resource_info info;
+ void *d3d_tex2d = NULL;
+
+#ifdef HAVE_VIRGL_D3D_INFO_EXT
+ struct virgl_renderer_resource_info_ext ext;
+ memset(&ext, 0, sizeof(ext));
+ ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext);
+ info = ext.base;
+ d3d_tex2d = ext.d3d_tex2d;
+#else
+ memset(&info, 0, sizeof(info));
ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
+#endif
if (ret == -1) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: illegal resource specified %d\n",
g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
info.width, info.height,
- ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height,
+ d3d_tex2d);
} else {
dpy_gfx_replace_surface(
g->parent_obj.scanout[ss.scanout_id].con, NULL);
int virtio_gpu_virgl_init(VirtIOGPU *g)
{
int ret;
+ uint32_t flags = 0;
+
+#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
+ if (qemu_egl_display) {
+ virtio_gpu_3d_cbs.version = 4;
+ virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display;
+ }
+#endif
+#ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE
+ if (qemu_egl_angle_d3d) {
+ flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE;
+ }
+#endif
- ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs);
+ ret = virgl_renderer_init(g, flags, &virtio_gpu_3d_cbs);
if (ret != 0) {
error_report("virgl could not be initialized: %d", ret);
return ret;
return height * stride;
}
+#ifdef WIN32
+static void
+win32_pixman_image_destroy(pixman_image_t *image, void *data)
+{
+ HANDLE handle = data;
+
+ qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn);
+}
+#endif
+
static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height);
if (res->hostmem + g->hostmem < g->conf_max_hostmem) {
+ void *bits = NULL;
+#ifdef WIN32
+ bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
+ if (!bits) {
+ goto end;
+ }
+#endif
res->image = pixman_image_create_bits(pformat,
c2d.width,
c2d.height,
- NULL, 0);
+ bits, res->hostmem / c2d.height);
+#ifdef WIN32
+ if (res->image) {
+ pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle);
+ }
+#endif
}
+#ifdef WIN32
+end:
+#endif
if (!res->image) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: resource creation failed %d %d %d\n",
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_simple_resource *res;
- int h;
+ int h, bpp;
uint32_t src_offset, dst_offset, stride;
- int bpp;
pixman_format_code_t format;
struct virtio_gpu_transfer_to_host_2d t2d;
+ void *img_data;
VIRTIO_GPU_FILL_CMD(t2d);
virtio_gpu_t2d_bswap(&t2d);
format = pixman_image_get_format(res->image);
bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
stride = pixman_image_get_stride(res->image);
+ img_data = pixman_image_get_data(res->image);
- if (t2d.offset || t2d.r.x || t2d.r.y ||
- t2d.r.width != pixman_image_get_width(res->image)) {
- void *img_data = pixman_image_get_data(res->image);
+ if (t2d.r.x || t2d.r.width != pixman_image_get_width(res->image)) {
for (h = 0; h < t2d.r.height; h++) {
src_offset = t2d.offset + stride * h;
dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
iov_to_buf(res->iov, res->iov_cnt, src_offset,
- (uint8_t *)img_data
- + dst_offset, t2d.r.width * bpp);
+ (uint8_t *)img_data + dst_offset,
+ t2d.r.width * bpp);
}
} else {
- iov_to_buf(res->iov, res->iov_cnt, 0,
- pixman_image_get_data(res->image),
- pixman_image_get_stride(res->image)
- * pixman_image_get_height(res->image));
+ src_offset = t2d.offset;
+ dst_offset = t2d.r.y * stride + t2d.r.x * bpp;
+ iov_to_buf(res->iov, res->iov_cnt, src_offset,
+ (uint8_t *)img_data + dst_offset,
+ stride * t2d.r.height);
}
}
struct virtio_gpu_resource_flush rf;
struct virtio_gpu_scanout *scanout;
pixman_region16_t flush_region;
+ bool within_bounds = false;
+ bool update_submitted = false;
int i;
VIRTIO_GPU_FILL_CMD(rf);
rf.r.x < scanout->x + scanout->width &&
rf.r.x + rf.r.width >= scanout->x &&
rf.r.y < scanout->y + scanout->height &&
- rf.r.y + rf.r.height >= scanout->y &&
- console_has_gl(scanout->con)) {
- dpy_gl_update(scanout->con, 0, 0, scanout->width,
- scanout->height);
+ rf.r.y + rf.r.height >= scanout->y) {
+ within_bounds = true;
+
+ if (console_has_gl(scanout->con)) {
+ dpy_gl_update(scanout->con, 0, 0, scanout->width,
+ scanout->height);
+ update_submitted = true;
+ }
}
}
- return;
+
+ if (update_submitted) {
+ return;
+ }
+ if (!within_bounds) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside scanouts"
+ " bounds for flush %d: %d %d %d %d\n",
+ __func__, rf.resource_id, rf.r.x, rf.r.y,
+ rf.r.width, rf.r.height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
}
if (!res->blob &&
if (console_has_gl(scanout->con)) {
if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) {
virtio_gpu_update_scanout(g, scanout_id, res, r);
- return;
+ } else {
+ *error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
}
+ return;
}
data = res->blob;
*error = VIRTIO_GPU_RESP_ERR_UNSPEC;
return;
}
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset);
+#endif
pixman_image_unref(rect);
dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con,
struct virtio_gpu_simple_resource *res;
struct virtio_gpu_scanout *scanout;
uint32_t resource_id, pformat;
+ void *bits = NULL;
int i;
g->hostmem = 0;
g_free(res);
return -EINVAL;
}
+
+ res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
+#ifdef WIN32
+ bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
+ if (!bits) {
+ g_free(res);
+ return -EINVAL;
+ }
+#endif
res->image = pixman_image_create_bits(pformat,
res->width, res->height,
- NULL, 0);
+ bits, res->hostmem / res->height);
if (!res->image) {
g_free(res);
return -EINVAL;
}
- res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
res->addrs = g_new(uint64_t, res->iov_cnt);
res->iov = g_new(struct iovec, res->iov_cnt);
if (!scanout->ds) {
return -EINVAL;
}
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
+#endif
dpy_gfx_replace_surface(scanout->con, scanout->ds);
dpy_gfx_update_full(scanout->con);
{
FWCfgState *fw_cfg;
uint64_t val;
+ const char qemu_version[] = QEMU_VERSION;
fw_cfg = fw_cfg_init_mem(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4);
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, ms->smp.cpus);
fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_config.order[0]);
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+ fw_cfg_add_file(fw_cfg, "/etc/qemu-version",
+ g_memdup(qemu_version, sizeof(qemu_version)),
+ sizeof(qemu_version));
+
return fw_cfg;
}
/* Start all CPUs at the firmware entry point.
* Monarch CPU will initialize firmware, secondary CPUs
- * will enter a small idle look and wait for rendevouz. */
+ * will enter a small idle loop and wait for rendevouz. */
for (i = 0; i < smp_cpus; i++) {
- cpu_set_pc(CPU(cpu[i]), firmware_entry);
+ CPUState *cs = CPU(cpu[i]);
+
+ cpu_set_pc(cs, firmware_entry);
+ cpu[i]->env.psw = PSW_Q;
cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000;
+
+ cs->exception_index = -1;
+ cs->halted = 0;
}
/* already initialized by machine_hppa_init()? */
return count;
}
+static uint32_t pnv_xive_presenter_get_config(XivePresenter *xptr)
+{
+ uint32_t cfg = 0;
+
+ /* TIMA GEN1 is all P9 knows */
+ cfg |= XIVE_PRESENTER_GEN1_TIMA_OS;
+
+ return cfg;
+}
+
static uint8_t pnv_xive_get_block_id(XiveRouter *xrtr)
{
return pnv_xive_block_id(PNV_XIVE(xrtr));
xnc->notify = pnv_xive_notify;
xpc->match_nvt = pnv_xive_match_nvt;
+ xpc->get_config = pnv_xive_presenter_get_config;
};
static const TypeInfo pnv_xive_info = {
return count;
}
+static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr)
+{
+ PnvXive2 *xive = PNV_XIVE2(xptr);
+ uint32_t cfg = 0;
+
+ if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS) {
+ cfg |= XIVE_PRESENTER_GEN1_TIMA_OS;
+ }
+ return cfg;
+}
+
static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr)
{
return pnv_xive2_block_id(PNV_XIVE2(xrtr));
/*
* TIMA ops
*/
-
-/*
- * Special TIMA offsets to handle accesses in a POWER10 way.
- *
- * Only the CAM line updates done by the hypervisor should be handled
- * specifically.
- */
-#define HV_PAGE_OFFSET (XIVE_TM_HV_PAGE << TM_SHIFT)
-#define HV_PUSH_OS_CTX_OFFSET (HV_PAGE_OFFSET | (TM_QW1_OS + TM_WORD2))
-#define HV_PULL_OS_CTX_OFFSET (HV_PAGE_OFFSET | TM_SPC_PULL_OS_CTX)
-
static void pnv_xive2_tm_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu);
XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
XivePresenter *xptr = XIVE_PRESENTER(xive);
- bool gen1_tima_os =
- xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS;
-
- offset &= TM_ADDRESS_MASK;
-
- /* TODO: should we switch the TM ops table instead ? */
- if (!gen1_tima_os && offset == HV_PUSH_OS_CTX_OFFSET) {
- xive2_tm_push_os_ctx(xptr, tctx, offset, value, size);
- return;
- }
- /* Other TM ops are the same as XIVE1 */
xive_tctx_tm_write(xptr, tctx, offset, value, size);
}
PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu);
XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
XivePresenter *xptr = XIVE_PRESENTER(xive);
- bool gen1_tima_os =
- xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS;
-
- offset &= TM_ADDRESS_MASK;
-
- /* TODO: should we switch the TM ops table instead ? */
- if (!gen1_tima_os && offset == HV_PULL_OS_CTX_OFFSET) {
- return xive2_tm_pull_os_ctx(xptr, tctx, offset, size);
- }
- /* Other TM ops are the same as XIVE1 */
return xive_tctx_tm_read(xptr, tctx, offset, size);
}
xnc->notify = pnv_xive2_notify;
xpc->match_nvt = pnv_xive2_match_nvt;
+ xpc->get_config = pnv_xive2_presenter_get_config;
};
static const TypeInfo pnv_xive2_info = {
return count;
}
+static uint32_t spapr_xive_presenter_get_config(XivePresenter *xptr)
+{
+ uint32_t cfg = 0;
+
+ /*
+ * Let's claim GEN1 TIMA format. If running with KVM on P10, the
+ * correct answer is deep in the hardware and not accessible to
+ * us. But it shouldn't matter as it only affects the presenter
+ * as seen by a guest OS.
+ */
+ cfg |= XIVE_PRESENTER_GEN1_TIMA_OS;
+
+ return cfg;
+}
+
static uint8_t spapr_xive_get_block_id(XiveRouter *xrtr)
{
return SPAPR_XIVE_BLOCK_ID;
sicc->post_load = spapr_xive_post_load;
xpc->match_nvt = spapr_xive_match_nvt;
+ xpc->get_config = spapr_xive_presenter_get_config;
xpc->in_kernel = spapr_xive_in_kernel_xptr;
}
#include "monitor/monitor.h"
#include "hw/irq.h"
#include "hw/ppc/xive.h"
+#include "hw/ppc/xive2.h"
#include "hw/ppc/xive_regs.h"
#include "trace.h"
}
}
+static uint32_t xive_presenter_get_config(XivePresenter *xptr)
+{
+ XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
+
+ return xpc->get_config(xptr);
+}
+
/*
* Define a mapping of "special" operations depending on the TIMA page
* offset and the size of the operation.
{ XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, xive_tm_pull_pool_ctx },
};
-static const XiveTmOp *xive_tm_find_op(hwaddr offset, unsigned size, bool write)
+static const XiveTmOp xive2_tm_operations[] = {
+ /*
+ * MMIOs below 2K : raw values and special operations without side
+ * effects
+ */
+ { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, xive_tm_vt_poll },
+
+ /* MMIOs above 2K : special operations with side effects */
+ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, xive_tm_ack_os_reg },
+ { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, NULL },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL, xive2_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, xive2_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, xive_tm_ack_hv_reg },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, xive_tm_pull_pool_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, xive_tm_pull_pool_ctx },
+};
+
+static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset,
+ unsigned size, bool write)
{
uint8_t page_offset = (offset >> TM_SHIFT) & 0x3;
uint32_t op_offset = offset & TM_ADDRESS_MASK;
- int i;
+ const XiveTmOp *tm_ops;
+ int i, tm_ops_count;
+ uint32_t cfg;
+
+ cfg = xive_presenter_get_config(xptr);
+ if (cfg & XIVE_PRESENTER_GEN1_TIMA_OS) {
+ tm_ops = xive_tm_operations;
+ tm_ops_count = ARRAY_SIZE(xive_tm_operations);
+ } else {
+ tm_ops = xive2_tm_operations;
+ tm_ops_count = ARRAY_SIZE(xive2_tm_operations);
+ }
- for (i = 0; i < ARRAY_SIZE(xive_tm_operations); i++) {
- const XiveTmOp *xto = &xive_tm_operations[i];
+ for (i = 0; i < tm_ops_count; i++) {
+ const XiveTmOp *xto = &tm_ops[i];
/* Accesses done from a more privileged TIMA page is allowed */
if (xto->page_offset >= page_offset &&
* First, check for special operations in the 2K region
*/
if (offset & TM_SPECIAL_OP) {
- xto = xive_tm_find_op(offset, size, true);
+ xto = xive_tm_find_op(tctx->xptr, offset, size, true);
if (!xto) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA "
"@%"HWADDR_PRIx"\n", offset);
/*
* Then, for special operations in the region below 2K.
*/
- xto = xive_tm_find_op(offset, size, true);
+ xto = xive_tm_find_op(tctx->xptr, offset, size, true);
if (xto) {
xto->write_handler(xptr, tctx, offset, value, size);
return;
* First, check for special operations in the 2K region
*/
if (offset & TM_SPECIAL_OP) {
- xto = xive_tm_find_op(offset, size, false);
+ xto = xive_tm_find_op(tctx->xptr, offset, size, false);
if (!xto) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA"
"@%"HWADDR_PRIx"\n", offset);
/*
* Then, for special operations in the region below 2K.
*/
- xto = xive_tm_find_op(offset, size, false);
+ xto = xive_tm_find_op(tctx->xptr, offset, size, false);
if (xto) {
ret = xto->read_handler(xptr, tctx, offset, size);
goto out;
* subsys=<subsys_id>
* -device nvme-ns,drive=<drive_id>,bus=<bus_name>,nsid=<nsid>,\
* zoned=<true|false[optional]>, \
- * subsys=<subsys_id>,detached=<true|false[optional]>
+ * subsys=<subsys_id>,shared=<true|false[optional]>, \
+ * detached=<true|false[optional]>, \
+ * zoned.zone_size=<N[optional]>, \
+ * zoned.zone_capacity=<N[optional]>, \
+ * zoned.descr_ext_size=<N[optional]>, \
+ * zoned.max_active=<N[optional]>, \
+ * zoned.max_open=<N[optional]>, \
+ * zoned.cross_read=<true|false[optional]>
*
* Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at
* offset 0 in BAR2 and supports only WDS, RDS and SQS for now. By default, the
case NVME_CMD_WRITE:
case NVME_CMD_WRITE_ZEROES:
case NVME_CMD_ZONE_APPEND:
+ case NVME_CMD_COPY:
status = NVME_WRITE_FAULT;
break;
default:
}
}
+static inline uint16_t nvme_check_copy_mcl(NvmeNamespace *ns,
+ NvmeCopyAIOCB *iocb, uint16_t nr)
+{
+ uint32_t copy_len = 0;
+
+ for (int idx = 0; idx < nr; idx++) {
+ uint32_t nlb;
+ nvme_copy_source_range_parse(iocb->ranges, idx, iocb->format, NULL,
+ &nlb, NULL, NULL, NULL);
+ copy_len += nlb + 1;
+ }
+
+ if (copy_len > ns->id_ns.mcl) {
+ return NVME_CMD_SIZE_LIMIT | NVME_DNR;
+ }
+
+ return NVME_SUCCESS;
+}
+
static void nvme_copy_out_completed_cb(void *opaque, int ret)
{
NvmeCopyAIOCB *iocb = opaque;
}
}
+ status = nvme_check_copy_mcl(ns, iocb, nr);
+ if (status) {
+ goto invalid;
+ }
+
iocb->req = req;
iocb->ret = 0;
iocb->nr = nr;
NvmeRuHandle *ruh;
uint8_t lbafi = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
g_autofree unsigned int *ruhids = NULL;
- unsigned int *ruhid;
- char *r, *p, *token;
+ unsigned int n, m, *ruhid;
+ const char *endptr, *token;
+ char *r, *p;
uint16_t *ph;
if (!ns->params.fdp.ruhs) {
/* parse the placement handle identifiers */
while ((token = qemu_strsep(&p, ";")) != NULL) {
- ns->fdp.nphs += 1;
- if (ns->fdp.nphs > NVME_FDP_MAXPIDS ||
- ns->fdp.nphs == endgrp->fdp.nruh) {
- error_setg(errp, "too many placement handles");
+ if (qemu_strtoui(token, &endptr, 0, &n) < 0) {
+ error_setg(errp, "cannot parse reclaim unit handle identifier");
free(r);
return false;
}
- if (qemu_strtoui(token, NULL, 0, ruhid++) < 0) {
- error_setg(errp, "cannot parse reclaim unit handle identifier");
- free(r);
- return false;
+ m = n;
+
+ /* parse range */
+ if (*endptr == '-') {
+ token = endptr + 1;
+
+ if (qemu_strtoui(token, NULL, 0, &m) < 0) {
+ error_setg(errp, "cannot parse reclaim unit handle identifier");
+ free(r);
+ return false;
+ }
+
+ if (m < n) {
+ error_setg(errp, "invalid reclaim unit handle identifier range");
+ free(r);
+ return false;
+ }
+ }
+
+ for (; n <= m; n++) {
+ if (ns->fdp.nphs++ == endgrp->fdp.nruh) {
+ error_setg(errp, "too many placement handles");
+ free(r);
+ return false;
+ }
+
+ *ruhid++ = n;
}
}
free(r);
+ /* verify that the ruhids are unique */
+ for (unsigned int i = 0; i < ns->fdp.nphs; i++) {
+ for (unsigned int j = i + 1; j < ns->fdp.nphs; j++) {
+ if (ruhids[i] == ruhids[j]) {
+ error_setg(errp, "duplicate reclaim unit handle identifier: %u",
+ ruhids[i]);
+ return false;
+ }
+ }
+ }
+
ph = ns->fdp.phs = g_new(uint16_t, ns->fdp.nphs);
ruhid = ruhids;
endgrp->fdp.nrg = subsys->params.fdp.nrg;
- if (!subsys->params.fdp.nruh) {
- error_setg(errp, "fdp.nruh must be non-zero");
+ if (!subsys->params.fdp.nruh ||
+ subsys->params.fdp.nruh > NVME_FDP_MAXPIDS) {
+ error_setg(errp, "fdp.nruh must be non-zero and less than %u",
+ NVME_FDP_MAXPIDS);
return false;
}
PCIDevice *pdev;
if (size != 4) {
- phb_error(phb, "rc_config_write invalid size %d\n", size);
+ phb_error(phb, "rc_config_write invalid size %d", size);
return;
}
pdev = pci_find_device(pci->bus, 0, 0);
if (!pdev) {
- phb_error(phb, "rc_config_write device not found\n");
+ phb_error(phb, "rc_config_write device not found");
return;
}
uint64_t val;
if (size != 4) {
- phb_error(phb, "rc_config_read invalid size %d\n", size);
+ phb_error(phb, "rc_config_read invalid size %d", size);
return ~0ull;
}
pdev = pci_find_device(pci->bus, 0, 0);
if (!pdev) {
- phb_error(phb, "rc_config_read device not found\n");
+ phb_error(phb, "rc_config_read device not found");
return ~0ull;
}
if (phb->nest_regs[PEC_NEST_STK_BAR_EN] &
(PEC_NEST_STK_BAR_EN_MMIO0 |
PEC_NEST_STK_BAR_EN_MMIO1)) {
- phb_pec_error(pec, "Changing enabled BAR unsupported\n");
+ phb_pec_error(pec, "Changing enabled BAR unsupported");
}
phb->nest_regs[reg] = val & 0xffffffffff000000ull;
break;
case PEC_NEST_STK_PHB_REGS_BAR:
if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
- phb_pec_error(pec, "Changing enabled BAR unsupported\n");
+ phb_pec_error(pec, "Changing enabled BAR unsupported");
}
phb->nest_regs[reg] = val & 0xffffffffffc00000ull;
break;
case PEC_NEST_STK_INT_BAR:
if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
- phb_pec_error(pec, "Changing enabled BAR unsupported\n");
+ phb_pec_error(pec, "Changing enabled BAR unsupported");
}
phb->nest_regs[reg] = val & 0xfffffff000000000ull;
break;
'spapr_vio.c',
'spapr_events.c',
'spapr_hcall.c',
+ 'spapr_nested.c',
'spapr_iommu.c',
'spapr_rtas.c',
'spapr_pci.c',
DeviceState *dev;
if (kvm_enabled()) {
- error_report("The powernv machine does not work with KVM acceleration");
+ error_report("machine %s does not support the KVM accelerator",
+ mc->name);
exit(EXIT_FAILURE);
}
return env->spr_cb[SPR_PIR].default_value;
}
+int ppc_cpu_tir(PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+ return env->spr_cb[SPR_TIR].default_value;
+}
+
PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
{
CPUState *cs;
#include "hw/pci/pci.h"
#include "hw/boards.h"
#include "sysemu/kvm.h"
-#include "kvm_ppc.h"
#include "sysemu/device_tree.h"
#include "hw/loader.h"
#include "elf.h"
fprintf(stderr, "couldn't set /chosen/bootargs\n");
}
- /*
- * Copy data from the host device tree into the guest. Since the guest can
- * directly access the timebase without host involvement, we must expose
- * the correct frequencies.
- */
- if (kvm_enabled()) {
- tb_freq = kvmppc_get_tbfreq();
- clock_freq = kvmppc_get_clockfreq();
- }
-
qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
clock_freq);
qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
int success;
int i;
+ if (kvm_enabled()) {
+ error_report("machine %s does not support the KVM accelerator",
+ MACHINE_GET_CLASS(machine)->name);
+ exit(EXIT_FAILURE);
+ }
+
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
env = &cpu->env;
#include "trace.h"
#include "elf.h"
#include "qemu/units.h"
-#include "kvm_ppc.h"
/* SMP is not enabled, for now */
#define MAX_CPUS 1
long kernel_size = 0, initrd_size = 0;
char boot_device;
+ if (kvm_enabled()) {
+ error_report("machine %s does not support the KVM accelerator",
+ MACHINE_GET_CLASS(machine)->name);
+ exit(EXIT_FAILURE);
+ }
+
/* init CPU */
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
env = &cpu->env;
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
- fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
- if (kvm_enabled()) {
- uint8_t *hypercall;
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
- hypercall = g_malloc(16);
- kvmppc_get_hypercall(env, hypercall, 16);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
- fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
- } else {
- fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND);
- }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND);
fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device);
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
#include "hw/ppc/fdt.h"
#include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_nested.h"
#include "hw/ppc/spapr_vio.h"
#include "hw/ppc/vof.h"
#include "hw/qdev-properties.h"
int ret;
unsigned int smp_threads = ms->smp.threads;
- if (!kvm_enabled() && (smp_threads > 1)) {
- error_setg(errp, "TCG cannot support more than 1 thread/core "
- "on a pseries machine");
- return;
+ if (tcg_enabled()) {
+ if (smp_threads > 1 &&
+ !ppc_type_check_compat(ms->cpu_type, CPU_POWERPC_LOGICAL_2_07, 0,
+ spapr->max_compat_pvr)) {
+ error_setg(errp, "TCG only supports SMT on POWER8 or newer CPUs");
+ return;
+ }
+
+ if (smp_threads > 8) {
+ error_setg(errp, "TCG cannot support more than 8 threads/core "
+ "on a pseries machine");
+ return;
+ }
}
if (!is_power_of_2(smp_threads)) {
error_setg(errp, "Cannot support %d threads/core on a pseries "
error_append_hint(errp,
"Try appending -machine cap-nested-hv=off\n");
}
+ } else if (tcg_enabled()) {
+ MachineState *ms = MACHINE(spapr);
+ unsigned int smp_threads = ms->smp.threads;
+
+ /*
+ * Nested-HV vCPU env state to L2, so SMT-shared SPR updates, for
+ * example, do not necessarily update the correct SPR value on sibling
+ * threads that are in a different guest/host context.
+ */
+ if (smp_threads > 1) {
+ error_setg(errp, "TCG does not support nested-HV with SMT");
+ error_append_hint(errp, "Try appending -machine cap-nested-hv=off "
+ "or use threads=1 with -smp\n");
+ }
}
}
}
static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr,
- SpaprCpuCore *sc, Error **errp)
+ SpaprCpuCore *sc, int thread_index, Error **errp)
{
CPUPPCState *env = &cpu->env;
CPUState *cs = CPU(cpu);
cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
kvmppc_set_papr(cpu);
+ env->spr_cb[SPR_PIR].default_value = cs->cpu_index;
+ env->spr_cb[SPR_TIR].default_value = thread_index;
+
/* Set time-base frequency to 512 MHz. vhyp must be set first. */
cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
for (i = 0; i < cc->nr_threads; i++) {
sc->threads[i] = spapr_create_vcpu(sc, i, errp);
if (!sc->threads[i] ||
- !spapr_realize_vcpu(sc->threads[i], spapr, sc, errp)) {
+ !spapr_realize_vcpu(sc->threads[i], spapr, sc, i, errp)) {
spapr_cpu_core_unrealize(dev);
return;
}
#include "hw/ppc/ppc.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_cpu_core.h"
+#include "hw/ppc/spapr_nested.h"
#include "mmu-hash64.h"
#include "cpu-models.h"
#include "trace.h"
}
#ifdef CONFIG_TCG
-#define PRTS_MASK 0x1f
-
-static target_ulong h_set_ptbl(PowerPCCPU *cpu,
- SpaprMachineState *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- target_ulong ptcr = args[0];
-
- if (!spapr_get_cap(spapr, SPAPR_CAP_NESTED_KVM_HV)) {
- return H_FUNCTION;
- }
-
- if ((ptcr & PRTS_MASK) + 12 - 4 > 12) {
- return H_PARAMETER;
- }
-
- spapr->nested_ptcr = ptcr; /* Save new partition table */
-
- return H_SUCCESS;
-}
-
-static target_ulong h_tlb_invalidate(PowerPCCPU *cpu,
- SpaprMachineState *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- /*
- * The spapr virtual hypervisor nested HV implementation retains no L2
- * translation state except for TLB. And the TLB is always invalidated
- * across L1<->L2 transitions, so nothing is required here.
- */
-
- return H_SUCCESS;
-}
-
-static target_ulong h_copy_tofrom_guest(PowerPCCPU *cpu,
- SpaprMachineState *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- /*
- * This HCALL is not required, L1 KVM will take a slow path and walk the
- * page tables manually to do the data copy.
- */
- return H_FUNCTION;
-}
-
-/*
- * When this handler returns, the environment is switched to the L2 guest
- * and TCG begins running that. spapr_exit_nested() performs the switch from
- * L2 back to L1 and returns from the H_ENTER_NESTED hcall.
- */
-static target_ulong h_enter_nested(PowerPCCPU *cpu,
- SpaprMachineState *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- CPUState *cs = CPU(cpu);
- CPUPPCState *env = &cpu->env;
- SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
- target_ulong hv_ptr = args[0];
- target_ulong regs_ptr = args[1];
- target_ulong hdec, now = cpu_ppc_load_tbl(env);
- target_ulong lpcr, lpcr_mask;
- struct kvmppc_hv_guest_state *hvstate;
- struct kvmppc_hv_guest_state hv_state;
- struct kvmppc_pt_regs *regs;
- hwaddr len;
-
- if (spapr->nested_ptcr == 0) {
- return H_NOT_AVAILABLE;
- }
-
- len = sizeof(*hvstate);
- hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, false,
- MEMTXATTRS_UNSPECIFIED);
- if (len != sizeof(*hvstate)) {
- address_space_unmap(CPU(cpu)->as, hvstate, len, 0, false);
- return H_PARAMETER;
- }
-
- memcpy(&hv_state, hvstate, len);
-
- address_space_unmap(CPU(cpu)->as, hvstate, len, len, false);
-
- /*
- * We accept versions 1 and 2. Version 2 fields are unused because TCG
- * does not implement DAWR*.
- */
- if (hv_state.version > HV_GUEST_STATE_VERSION) {
- return H_PARAMETER;
- }
-
- spapr_cpu->nested_host_state = g_try_new(CPUPPCState, 1);
- if (!spapr_cpu->nested_host_state) {
- return H_NO_MEM;
- }
-
- memcpy(spapr_cpu->nested_host_state, env, sizeof(CPUPPCState));
-
- len = sizeof(*regs);
- regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, false,
- MEMTXATTRS_UNSPECIFIED);
- if (!regs || len != sizeof(*regs)) {
- address_space_unmap(CPU(cpu)->as, regs, len, 0, false);
- g_free(spapr_cpu->nested_host_state);
- return H_P2;
- }
-
- len = sizeof(env->gpr);
- assert(len == sizeof(regs->gpr));
- memcpy(env->gpr, regs->gpr, len);
-
- env->lr = regs->link;
- env->ctr = regs->ctr;
- cpu_write_xer(env, regs->xer);
- ppc_set_cr(env, regs->ccr);
-
- env->msr = regs->msr;
- env->nip = regs->nip;
-
- address_space_unmap(CPU(cpu)->as, regs, len, len, false);
-
- env->cfar = hv_state.cfar;
-
- assert(env->spr[SPR_LPIDR] == 0);
- env->spr[SPR_LPIDR] = hv_state.lpid;
-
- lpcr_mask = LPCR_DPFD | LPCR_ILE | LPCR_AIL | LPCR_LD | LPCR_MER;
- lpcr = (env->spr[SPR_LPCR] & ~lpcr_mask) | (hv_state.lpcr & lpcr_mask);
- lpcr |= LPCR_HR | LPCR_UPRT | LPCR_GTSE | LPCR_HVICE | LPCR_HDICE;
- lpcr &= ~LPCR_LPES0;
- env->spr[SPR_LPCR] = lpcr & pcc->lpcr_mask;
-
- env->spr[SPR_PCR] = hv_state.pcr;
- /* hv_state.amor is not used */
- env->spr[SPR_DPDES] = hv_state.dpdes;
- env->spr[SPR_HFSCR] = hv_state.hfscr;
- hdec = hv_state.hdec_expiry - now;
- spapr_cpu->nested_tb_offset = hv_state.tb_offset;
- /* TCG does not implement DAWR*, CIABR, PURR, SPURR, IC, VTB, HEIR SPRs*/
- env->spr[SPR_SRR0] = hv_state.srr0;
- env->spr[SPR_SRR1] = hv_state.srr1;
- env->spr[SPR_SPRG0] = hv_state.sprg[0];
- env->spr[SPR_SPRG1] = hv_state.sprg[1];
- env->spr[SPR_SPRG2] = hv_state.sprg[2];
- env->spr[SPR_SPRG3] = hv_state.sprg[3];
- env->spr[SPR_BOOKS_PID] = hv_state.pidr;
- env->spr[SPR_PPR] = hv_state.ppr;
-
- cpu_ppc_hdecr_init(env);
- cpu_ppc_store_hdecr(env, hdec);
-
- /*
- * The hv_state.vcpu_token is not needed. It is used by the KVM
- * implementation to remember which L2 vCPU last ran on which physical
- * CPU so as to invalidate process scope translations if it is moved
- * between physical CPUs. For now TLBs are always flushed on L1<->L2
- * transitions so this is not a problem.
- *
- * Could validate that the same vcpu_token does not attempt to run on
- * different L1 vCPUs at the same time, but that would be a L1 KVM bug
- * and it's not obviously worth a new data structure to do it.
- */
-
- env->tb_env->tb_offset += spapr_cpu->nested_tb_offset;
- spapr_cpu->in_nested = true;
-
- hreg_compute_hflags(env);
- ppc_maybe_interrupt(env);
- tlb_flush(cs);
- env->reserve_addr = -1; /* Reset the reservation */
-
- /*
- * The spapr hcall helper sets env->gpr[3] to the return value, but at
- * this point the L1 is not returning from the hcall but rather we
- * start running the L2, so r3 must not be clobbered, so return env->gpr[3]
- * to leave it unchanged.
- */
- return env->gpr[3];
-}
-
-void spapr_exit_nested(PowerPCCPU *cpu, int excp)
-{
- CPUState *cs = CPU(cpu);
- CPUPPCState *env = &cpu->env;
- SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
- target_ulong r3_return = env->excp_vectors[excp]; /* hcall return value */
- target_ulong hv_ptr = spapr_cpu->nested_host_state->gpr[4];
- target_ulong regs_ptr = spapr_cpu->nested_host_state->gpr[5];
- struct kvmppc_hv_guest_state *hvstate;
- struct kvmppc_pt_regs *regs;
- hwaddr len;
-
- assert(spapr_cpu->in_nested);
-
- cpu_ppc_hdecr_exit(env);
-
- len = sizeof(*hvstate);
- hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, true,
- MEMTXATTRS_UNSPECIFIED);
- if (len != sizeof(*hvstate)) {
- address_space_unmap(CPU(cpu)->as, hvstate, len, 0, true);
- r3_return = H_PARAMETER;
- goto out_restore_l1;
- }
-
- hvstate->cfar = env->cfar;
- hvstate->lpcr = env->spr[SPR_LPCR];
- hvstate->pcr = env->spr[SPR_PCR];
- hvstate->dpdes = env->spr[SPR_DPDES];
- hvstate->hfscr = env->spr[SPR_HFSCR];
-
- if (excp == POWERPC_EXCP_HDSI) {
- hvstate->hdar = env->spr[SPR_HDAR];
- hvstate->hdsisr = env->spr[SPR_HDSISR];
- hvstate->asdr = env->spr[SPR_ASDR];
- } else if (excp == POWERPC_EXCP_HISI) {
- hvstate->asdr = env->spr[SPR_ASDR];
- }
-
- /* HEIR should be implemented for HV mode and saved here. */
- hvstate->srr0 = env->spr[SPR_SRR0];
- hvstate->srr1 = env->spr[SPR_SRR1];
- hvstate->sprg[0] = env->spr[SPR_SPRG0];
- hvstate->sprg[1] = env->spr[SPR_SPRG1];
- hvstate->sprg[2] = env->spr[SPR_SPRG2];
- hvstate->sprg[3] = env->spr[SPR_SPRG3];
- hvstate->pidr = env->spr[SPR_BOOKS_PID];
- hvstate->ppr = env->spr[SPR_PPR];
-
- /* Is it okay to specify write length larger than actual data written? */
- address_space_unmap(CPU(cpu)->as, hvstate, len, len, true);
-
- len = sizeof(*regs);
- regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, true,
- MEMTXATTRS_UNSPECIFIED);
- if (!regs || len != sizeof(*regs)) {
- address_space_unmap(CPU(cpu)->as, regs, len, 0, true);
- r3_return = H_P2;
- goto out_restore_l1;
- }
-
- len = sizeof(env->gpr);
- assert(len == sizeof(regs->gpr));
- memcpy(regs->gpr, env->gpr, len);
-
- regs->link = env->lr;
- regs->ctr = env->ctr;
- regs->xer = cpu_read_xer(env);
- regs->ccr = ppc_get_cr(env);
-
- if (excp == POWERPC_EXCP_MCHECK ||
- excp == POWERPC_EXCP_RESET ||
- excp == POWERPC_EXCP_SYSCALL) {
- regs->nip = env->spr[SPR_SRR0];
- regs->msr = env->spr[SPR_SRR1] & env->msr_mask;
- } else {
- regs->nip = env->spr[SPR_HSRR0];
- regs->msr = env->spr[SPR_HSRR1] & env->msr_mask;
- }
-
- /* Is it okay to specify write length larger than actual data written? */
- address_space_unmap(CPU(cpu)->as, regs, len, len, true);
-
-out_restore_l1:
- memcpy(env->gpr, spapr_cpu->nested_host_state->gpr, sizeof(env->gpr));
- env->lr = spapr_cpu->nested_host_state->lr;
- env->ctr = spapr_cpu->nested_host_state->ctr;
- memcpy(env->crf, spapr_cpu->nested_host_state->crf, sizeof(env->crf));
- env->cfar = spapr_cpu->nested_host_state->cfar;
- env->xer = spapr_cpu->nested_host_state->xer;
- env->so = spapr_cpu->nested_host_state->so;
- env->ov = spapr_cpu->nested_host_state->ov;
- env->ov32 = spapr_cpu->nested_host_state->ov32;
- env->ca32 = spapr_cpu->nested_host_state->ca32;
- env->msr = spapr_cpu->nested_host_state->msr;
- env->nip = spapr_cpu->nested_host_state->nip;
-
- assert(env->spr[SPR_LPIDR] != 0);
- env->spr[SPR_LPCR] = spapr_cpu->nested_host_state->spr[SPR_LPCR];
- env->spr[SPR_LPIDR] = spapr_cpu->nested_host_state->spr[SPR_LPIDR];
- env->spr[SPR_PCR] = spapr_cpu->nested_host_state->spr[SPR_PCR];
- env->spr[SPR_DPDES] = 0;
- env->spr[SPR_HFSCR] = spapr_cpu->nested_host_state->spr[SPR_HFSCR];
- env->spr[SPR_SRR0] = spapr_cpu->nested_host_state->spr[SPR_SRR0];
- env->spr[SPR_SRR1] = spapr_cpu->nested_host_state->spr[SPR_SRR1];
- env->spr[SPR_SPRG0] = spapr_cpu->nested_host_state->spr[SPR_SPRG0];
- env->spr[SPR_SPRG1] = spapr_cpu->nested_host_state->spr[SPR_SPRG1];
- env->spr[SPR_SPRG2] = spapr_cpu->nested_host_state->spr[SPR_SPRG2];
- env->spr[SPR_SPRG3] = spapr_cpu->nested_host_state->spr[SPR_SPRG3];
- env->spr[SPR_BOOKS_PID] = spapr_cpu->nested_host_state->spr[SPR_BOOKS_PID];
- env->spr[SPR_PPR] = spapr_cpu->nested_host_state->spr[SPR_PPR];
-
- /*
- * Return the interrupt vector address from H_ENTER_NESTED to the L1
- * (or error code).
- */
- env->gpr[3] = r3_return;
-
- env->tb_env->tb_offset -= spapr_cpu->nested_tb_offset;
- spapr_cpu->in_nested = false;
-
- hreg_compute_hflags(env);
- ppc_maybe_interrupt(env);
- tlb_flush(cs);
- env->reserve_addr = -1; /* Reset the reservation */
-
- g_free(spapr_cpu->nested_host_state);
- spapr_cpu->nested_host_state = NULL;
-}
-
-static void hypercall_register_nested(void)
-{
- spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl);
- spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested);
- spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate);
- spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest);
-}
-
static void hypercall_register_softmmu(void)
{
/* DO NOTHING */
}
#else
-void spapr_exit_nested(PowerPCCPU *cpu, int excp)
-{
- g_assert_not_reached();
-}
-
static target_ulong h_softmmu(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
g_assert_not_reached();
}
-static void hypercall_register_nested(void)
-{
- /* DO NOTHING */
-}
-
static void hypercall_register_softmmu(void)
{
/* hcall-pft */
spapr_register_hypercall(KVMPPC_H_UPDATE_DT, h_update_dt);
- hypercall_register_nested();
+ spapr_register_nested();
}
type_init(hypercall_register_types)
--- /dev/null
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "exec/exec-all.h"
+#include "helper_regs.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_cpu_core.h"
+#include "hw/ppc/spapr_nested.h"
+
+#ifdef CONFIG_TCG
+#define PRTS_MASK 0x1f
+
+static target_ulong h_set_ptbl(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ target_ulong ptcr = args[0];
+
+ if (!spapr_get_cap(spapr, SPAPR_CAP_NESTED_KVM_HV)) {
+ return H_FUNCTION;
+ }
+
+ if ((ptcr & PRTS_MASK) + 12 - 4 > 12) {
+ return H_PARAMETER;
+ }
+
+ spapr->nested_ptcr = ptcr; /* Save new partition table */
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_tlb_invalidate(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ /*
+ * The spapr virtual hypervisor nested HV implementation retains no L2
+ * translation state except for TLB. And the TLB is always invalidated
+ * across L1<->L2 transitions, so nothing is required here.
+ */
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_copy_tofrom_guest(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ /*
+ * This HCALL is not required, L1 KVM will take a slow path and walk the
+ * page tables manually to do the data copy.
+ */
+ return H_FUNCTION;
+}
+
+static void nested_save_state(struct nested_ppc_state *save, PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+
+ memcpy(save->gpr, env->gpr, sizeof(save->gpr));
+
+ save->lr = env->lr;
+ save->ctr = env->ctr;
+ save->cfar = env->cfar;
+ save->msr = env->msr;
+ save->nip = env->nip;
+
+ save->cr = ppc_get_cr(env);
+ save->xer = cpu_read_xer(env);
+
+ save->lpcr = env->spr[SPR_LPCR];
+ save->lpidr = env->spr[SPR_LPIDR];
+ save->pcr = env->spr[SPR_PCR];
+ save->dpdes = env->spr[SPR_DPDES];
+ save->hfscr = env->spr[SPR_HFSCR];
+ save->srr0 = env->spr[SPR_SRR0];
+ save->srr1 = env->spr[SPR_SRR1];
+ save->sprg0 = env->spr[SPR_SPRG0];
+ save->sprg1 = env->spr[SPR_SPRG1];
+ save->sprg2 = env->spr[SPR_SPRG2];
+ save->sprg3 = env->spr[SPR_SPRG3];
+ save->pidr = env->spr[SPR_BOOKS_PID];
+ save->ppr = env->spr[SPR_PPR];
+
+ save->tb_offset = env->tb_env->tb_offset;
+}
+
+static void nested_load_state(PowerPCCPU *cpu, struct nested_ppc_state *load)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+
+ memcpy(env->gpr, load->gpr, sizeof(env->gpr));
+
+ env->lr = load->lr;
+ env->ctr = load->ctr;
+ env->cfar = load->cfar;
+ env->msr = load->msr;
+ env->nip = load->nip;
+
+ ppc_set_cr(env, load->cr);
+ cpu_write_xer(env, load->xer);
+
+ env->spr[SPR_LPCR] = load->lpcr;
+ env->spr[SPR_LPIDR] = load->lpidr;
+ env->spr[SPR_PCR] = load->pcr;
+ env->spr[SPR_DPDES] = load->dpdes;
+ env->spr[SPR_HFSCR] = load->hfscr;
+ env->spr[SPR_SRR0] = load->srr0;
+ env->spr[SPR_SRR1] = load->srr1;
+ env->spr[SPR_SPRG0] = load->sprg0;
+ env->spr[SPR_SPRG1] = load->sprg1;
+ env->spr[SPR_SPRG2] = load->sprg2;
+ env->spr[SPR_SPRG3] = load->sprg3;
+ env->spr[SPR_BOOKS_PID] = load->pidr;
+ env->spr[SPR_PPR] = load->ppr;
+
+ env->tb_env->tb_offset = load->tb_offset;
+
+ /*
+ * MSR updated, compute hflags and possible interrupts.
+ */
+ hreg_compute_hflags(env);
+ ppc_maybe_interrupt(env);
+
+ /*
+ * Nested HV does not tag TLB entries between L1 and L2, so must
+ * flush on transition.
+ */
+ tlb_flush(cs);
+ env->reserve_addr = -1; /* Reset the reservation */
+}
+
+/*
+ * When this handler returns, the environment is switched to the L2 guest
+ * and TCG begins running that. spapr_exit_nested() performs the switch from
+ * L2 back to L1 and returns from the H_ENTER_NESTED hcall.
+ */
+static target_ulong h_enter_nested(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ CPUPPCState *env = &cpu->env;
+ SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
+ struct nested_ppc_state l2_state;
+ target_ulong hv_ptr = args[0];
+ target_ulong regs_ptr = args[1];
+ target_ulong hdec, now = cpu_ppc_load_tbl(env);
+ target_ulong lpcr, lpcr_mask;
+ struct kvmppc_hv_guest_state *hvstate;
+ struct kvmppc_hv_guest_state hv_state;
+ struct kvmppc_pt_regs *regs;
+ hwaddr len;
+
+ if (spapr->nested_ptcr == 0) {
+ return H_NOT_AVAILABLE;
+ }
+
+ len = sizeof(*hvstate);
+ hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, false,
+ MEMTXATTRS_UNSPECIFIED);
+ if (len != sizeof(*hvstate)) {
+ address_space_unmap(CPU(cpu)->as, hvstate, len, 0, false);
+ return H_PARAMETER;
+ }
+
+ memcpy(&hv_state, hvstate, len);
+
+ address_space_unmap(CPU(cpu)->as, hvstate, len, len, false);
+
+ /*
+ * We accept versions 1 and 2. Version 2 fields are unused because TCG
+ * does not implement DAWR*.
+ */
+ if (hv_state.version > HV_GUEST_STATE_VERSION) {
+ return H_PARAMETER;
+ }
+
+ if (hv_state.lpid == 0) {
+ return H_PARAMETER;
+ }
+
+ spapr_cpu->nested_host_state = g_try_new(struct nested_ppc_state, 1);
+ if (!spapr_cpu->nested_host_state) {
+ return H_NO_MEM;
+ }
+
+ assert(env->spr[SPR_LPIDR] == 0);
+ assert(env->spr[SPR_DPDES] == 0);
+ nested_save_state(spapr_cpu->nested_host_state, cpu);
+
+ len = sizeof(*regs);
+ regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, false,
+ MEMTXATTRS_UNSPECIFIED);
+ if (!regs || len != sizeof(*regs)) {
+ address_space_unmap(CPU(cpu)->as, regs, len, 0, false);
+ g_free(spapr_cpu->nested_host_state);
+ return H_P2;
+ }
+
+ len = sizeof(l2_state.gpr);
+ assert(len == sizeof(regs->gpr));
+ memcpy(l2_state.gpr, regs->gpr, len);
+
+ l2_state.lr = regs->link;
+ l2_state.ctr = regs->ctr;
+ l2_state.xer = regs->xer;
+ l2_state.cr = regs->ccr;
+ l2_state.msr = regs->msr;
+ l2_state.nip = regs->nip;
+
+ address_space_unmap(CPU(cpu)->as, regs, len, len, false);
+
+ l2_state.cfar = hv_state.cfar;
+ l2_state.lpidr = hv_state.lpid;
+
+ lpcr_mask = LPCR_DPFD | LPCR_ILE | LPCR_AIL | LPCR_LD | LPCR_MER;
+ lpcr = (env->spr[SPR_LPCR] & ~lpcr_mask) | (hv_state.lpcr & lpcr_mask);
+ lpcr |= LPCR_HR | LPCR_UPRT | LPCR_GTSE | LPCR_HVICE | LPCR_HDICE;
+ lpcr &= ~LPCR_LPES0;
+ l2_state.lpcr = lpcr & pcc->lpcr_mask;
+
+ l2_state.pcr = hv_state.pcr;
+ /* hv_state.amor is not used */
+ l2_state.dpdes = hv_state.dpdes;
+ l2_state.hfscr = hv_state.hfscr;
+ /* TCG does not implement DAWR*, CIABR, PURR, SPURR, IC, VTB, HEIR SPRs*/
+ l2_state.srr0 = hv_state.srr0;
+ l2_state.srr1 = hv_state.srr1;
+ l2_state.sprg0 = hv_state.sprg[0];
+ l2_state.sprg1 = hv_state.sprg[1];
+ l2_state.sprg2 = hv_state.sprg[2];
+ l2_state.sprg3 = hv_state.sprg[3];
+ l2_state.pidr = hv_state.pidr;
+ l2_state.ppr = hv_state.ppr;
+ l2_state.tb_offset = env->tb_env->tb_offset + hv_state.tb_offset;
+
+ /*
+ * Switch to the nested guest environment and start the "hdec" timer.
+ */
+ nested_load_state(cpu, &l2_state);
+
+ hdec = hv_state.hdec_expiry - now;
+ cpu_ppc_hdecr_init(env);
+ cpu_ppc_store_hdecr(env, hdec);
+
+ /*
+ * The hv_state.vcpu_token is not needed. It is used by the KVM
+ * implementation to remember which L2 vCPU last ran on which physical
+ * CPU so as to invalidate process scope translations if it is moved
+ * between physical CPUs. For now TLBs are always flushed on L1<->L2
+ * transitions so this is not a problem.
+ *
+ * Could validate that the same vcpu_token does not attempt to run on
+ * different L1 vCPUs at the same time, but that would be a L1 KVM bug
+ * and it's not obviously worth a new data structure to do it.
+ */
+
+ spapr_cpu->in_nested = true;
+
+ /*
+ * The spapr hcall helper sets env->gpr[3] to the return value, but at
+ * this point the L1 is not returning from the hcall but rather we
+ * start running the L2, so r3 must not be clobbered, so return env->gpr[3]
+ * to leave it unchanged.
+ */
+ return env->gpr[3];
+}
+
+void spapr_exit_nested(PowerPCCPU *cpu, int excp)
+{
+ CPUPPCState *env = &cpu->env;
+ SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
+ struct nested_ppc_state l2_state;
+ target_ulong hv_ptr = spapr_cpu->nested_host_state->gpr[4];
+ target_ulong regs_ptr = spapr_cpu->nested_host_state->gpr[5];
+ target_ulong hsrr0, hsrr1, hdar, asdr, hdsisr;
+ struct kvmppc_hv_guest_state *hvstate;
+ struct kvmppc_pt_regs *regs;
+ hwaddr len;
+
+ assert(spapr_cpu->in_nested);
+
+ nested_save_state(&l2_state, cpu);
+ hsrr0 = env->spr[SPR_HSRR0];
+ hsrr1 = env->spr[SPR_HSRR1];
+ hdar = env->spr[SPR_HDAR];
+ hdsisr = env->spr[SPR_HDSISR];
+ asdr = env->spr[SPR_ASDR];
+
+ /*
+ * Switch back to the host environment (including for any error).
+ */
+ assert(env->spr[SPR_LPIDR] != 0);
+ nested_load_state(cpu, spapr_cpu->nested_host_state);
+ env->gpr[3] = env->excp_vectors[excp]; /* hcall return value */
+
+ cpu_ppc_hdecr_exit(env);
+
+ spapr_cpu->in_nested = false;
+
+ g_free(spapr_cpu->nested_host_state);
+ spapr_cpu->nested_host_state = NULL;
+
+ len = sizeof(*hvstate);
+ hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, true,
+ MEMTXATTRS_UNSPECIFIED);
+ if (len != sizeof(*hvstate)) {
+ address_space_unmap(CPU(cpu)->as, hvstate, len, 0, true);
+ env->gpr[3] = H_PARAMETER;
+ return;
+ }
+
+ hvstate->cfar = l2_state.cfar;
+ hvstate->lpcr = l2_state.lpcr;
+ hvstate->pcr = l2_state.pcr;
+ hvstate->dpdes = l2_state.dpdes;
+ hvstate->hfscr = l2_state.hfscr;
+
+ if (excp == POWERPC_EXCP_HDSI) {
+ hvstate->hdar = hdar;
+ hvstate->hdsisr = hdsisr;
+ hvstate->asdr = asdr;
+ } else if (excp == POWERPC_EXCP_HISI) {
+ hvstate->asdr = asdr;
+ }
+
+ /* HEIR should be implemented for HV mode and saved here. */
+ hvstate->srr0 = l2_state.srr0;
+ hvstate->srr1 = l2_state.srr1;
+ hvstate->sprg[0] = l2_state.sprg0;
+ hvstate->sprg[1] = l2_state.sprg1;
+ hvstate->sprg[2] = l2_state.sprg2;
+ hvstate->sprg[3] = l2_state.sprg3;
+ hvstate->pidr = l2_state.pidr;
+ hvstate->ppr = l2_state.ppr;
+
+ /* Is it okay to specify write length larger than actual data written? */
+ address_space_unmap(CPU(cpu)->as, hvstate, len, len, true);
+
+ len = sizeof(*regs);
+ regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, true,
+ MEMTXATTRS_UNSPECIFIED);
+ if (!regs || len != sizeof(*regs)) {
+ address_space_unmap(CPU(cpu)->as, regs, len, 0, true);
+ env->gpr[3] = H_P2;
+ return;
+ }
+
+ len = sizeof(env->gpr);
+ assert(len == sizeof(regs->gpr));
+ memcpy(regs->gpr, l2_state.gpr, len);
+
+ regs->link = l2_state.lr;
+ regs->ctr = l2_state.ctr;
+ regs->xer = l2_state.xer;
+ regs->ccr = l2_state.cr;
+
+ if (excp == POWERPC_EXCP_MCHECK ||
+ excp == POWERPC_EXCP_RESET ||
+ excp == POWERPC_EXCP_SYSCALL) {
+ regs->nip = l2_state.srr0;
+ regs->msr = l2_state.srr1 & env->msr_mask;
+ } else {
+ regs->nip = hsrr0;
+ regs->msr = hsrr1 & env->msr_mask;
+ }
+
+ /* Is it okay to specify write length larger than actual data written? */
+ address_space_unmap(CPU(cpu)->as, regs, len, len, true);
+}
+
+void spapr_register_nested(void)
+{
+ spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl);
+ spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested);
+ spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate);
+ spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest);
+}
+#else
+void spapr_exit_nested(PowerPCCPU *cpu, int excp)
+{
+ g_assert_not_reached();
+}
+
+void spapr_register_nested(void)
+{
+ /* DO NOTHING */
+}
+#endif
#include "qom/object.h"
#include "qemu/event_notifier.h"
#include "sysemu/kvm.h"
-#include "util/event_notifier-posix.c"
static void probe_pci_info(PCIDevice *dev, Error **errp);
static void proxy_device_reset(DeviceState *dev);
mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props;
mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id;
mc->numa_mem_supported = true;
+ /* platform instead of architectural choice */
+ mc->cpu_cluster_has_numa_boundary = true;
mc->default_ram_id = "riscv.spike.ram";
object_class_property_add_str(oc, "signature", NULL, spike_set_signature);
object_class_property_set_description(oc, "signature",
mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props;
mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id;
mc->numa_mem_supported = true;
+ /* platform instead of architectural choice */
+ mc->cpu_cluster_has_numa_boundary = true;
mc->default_ram_id = "riscv_virt_board.ram";
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
#define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS_MIN - 2))
/* Set if TLB entry is an IO callback. */
#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 3))
-/* Set if TLB entry contains a watchpoint. */
-#define TLB_WATCHPOINT (1 << (TARGET_PAGE_BITS_MIN - 4))
-/* Set if TLB entry requires byte swap. */
-#define TLB_BSWAP (1 << (TARGET_PAGE_BITS_MIN - 5))
/* Set if TLB entry writes ignored. */
-#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 6))
+#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 4))
+/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */
+#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 5))
-/* Use this mask to check interception with an alignment mask
+/*
+ * Use this mask to check interception with an alignment mask
* in a TCG backend.
*/
#define TLB_FLAGS_MASK \
(TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \
- | TLB_WATCHPOINT | TLB_BSWAP | TLB_DISCARD_WRITE)
+ | TLB_FORCE_SLOW | TLB_DISCARD_WRITE)
+
+/*
+ * Flags stored in CPUTLBEntryFull.slow_flags[x].
+ * TLB_FORCE_SLOW must be set in CPUTLBEntry.addr_idx[x].
+ */
+/* Set if TLB entry requires byte swap. */
+#define TLB_BSWAP (1 << 0)
+/* Set if TLB entry contains a watchpoint. */
+#define TLB_WATCHPOINT (1 << 1)
+
+#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT)
+
+/* The two sets of flags must not overlap. */
+QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK);
/**
* tlb_hit_page: return true if page aligned @addr is a hit against the
/* @lg_page_size contains the log2 of the page size. */
uint8_t lg_page_size;
+ /*
+ * Additional tlb flags for use by the slow path. If non-zero,
+ * the corresponding CPUTLBEntry comparator must have TLB_FORCE_SLOW.
+ */
+ uint8_t slow_flags[MMU_ACCESS_COUNT];
+
/*
* Allow target-specific additions to this structure.
* This may be used to cache items from the guest cpu
* we must flush the entire tlb. The region is matched if
* (addr & large_page_mask) == large_page_addr.
*/
- target_ulong large_page_addr;
- target_ulong large_page_mask;
+ vaddr large_page_addr;
+ vaddr large_page_mask;
/* host time (in ns) at the beginning of the time window */
int64_t window_begin_ns;
/* maximum number of entries observed in the window */
#include "tcg/oversized-guest.h"
-static inline target_ulong tlb_read_idx(const CPUTLBEntry *entry,
- MMUAccessType access_type)
+static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry,
+ MMUAccessType access_type)
{
/* Do not rearrange the CPUTLBEntry structure members. */
QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) !=
#endif
}
-static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry)
+static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry)
{
return tlb_read_idx(entry, MMU_DATA_STORE);
}
/* Find the TLB index corresponding to the mmu_idx + address pair. */
static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx,
- target_ulong addr)
+ vaddr addr)
{
uintptr_t size_mask = env_tlb(env)->f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS;
/* Find the TLB entry corresponding to the mmu_idx + address pair. */
static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx,
- target_ulong addr)
+ vaddr addr)
{
return &env_tlb(env)->f[mmu_idx].table[tlb_index(env, mmu_idx, addr)];
}
* Flush one page from the TLB of the specified CPU, for all
* MMU indexes.
*/
-void tlb_flush_page(CPUState *cpu, target_ulong addr);
+void tlb_flush_page(CPUState *cpu, vaddr addr);
/**
* tlb_flush_page_all_cpus:
* @cpu: src CPU of the flush
* Flush one page from the TLB of the specified CPU, for all
* MMU indexes.
*/
-void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr);
+void tlb_flush_page_all_cpus(CPUState *src, vaddr addr);
/**
* tlb_flush_page_all_cpus_synced:
* @cpu: src CPU of the flush
* the source vCPUs safe work is complete. This will depend on when
* the guests translation ends the TB.
*/
-void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr);
+void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr);
/**
* tlb_flush:
* @cpu: CPU whose TLB should be flushed
* Flush one page from the TLB of the specified CPU, for the specified
* MMU indexes.
*/
-void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr,
+void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr,
uint16_t idxmap);
/**
* tlb_flush_page_by_mmuidx_all_cpus:
* Flush one page from the TLB of all CPUs, for the specified
* MMU indexes.
*/
-void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr,
+void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr,
uint16_t idxmap);
/**
* tlb_flush_page_by_mmuidx_all_cpus_synced:
* complete once the source vCPUs safe work is complete. This will
* depend on when the guests translation ends the TB.
*/
-void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, target_ulong addr,
+void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr,
uint16_t idxmap);
/**
* tlb_flush_by_mmuidx:
*
* Similar to tlb_flush_page_mask, but with a bitmap of indexes.
*/
-void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr,
+void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
uint16_t idxmap, unsigned bits);
/* Similarly, with broadcast and syncing. */
-void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr,
+void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr,
uint16_t idxmap, unsigned bits);
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced
- (CPUState *cpu, target_ulong addr, uint16_t idxmap, unsigned bits);
+ (CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits);
/**
* tlb_flush_range_by_mmuidx
* For each mmuidx in @idxmap, flush all pages within [@addr,@addr+@len),
* comparing only the low @bits worth of each virtual page.
*/
-void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr,
- target_ulong len, uint16_t idxmap,
+void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
+ vaddr len, uint16_t idxmap,
unsigned bits);
/* Similarly, with broadcast and syncing. */
-void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr,
- target_ulong len, uint16_t idxmap,
+void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr,
+ vaddr len, uint16_t idxmap,
unsigned bits);
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu,
- target_ulong addr,
- target_ulong len,
+ vaddr addr,
+ vaddr len,
uint16_t idxmap,
unsigned bits);
* tlb_set_page_full:
* @cpu: CPU context
* @mmu_idx: mmu index of the tlb to modify
- * @vaddr: virtual address of the entry to add
+ * @addr: virtual address of the entry to add
* @full: the details of the tlb entry
*
* Add an entry to @cpu tlb index @mmu_idx. All of the fields of
* single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only
* used by tlb_flush_page.
*/
-void tlb_set_page_full(CPUState *cpu, int mmu_idx, target_ulong vaddr,
+void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr,
CPUTLBEntryFull *full);
/**
* tlb_set_page_with_attrs:
* @cpu: CPU to add this TLB entry for
- * @vaddr: virtual address of page to add entry for
+ * @addr: virtual address of page to add entry for
* @paddr: physical address of the page
* @attrs: memory transaction attributes
* @prot: access permissions (PAGE_READ/PAGE_WRITE/PAGE_EXEC bits)
* @size: size of the page in bytes
*
* Add an entry to this CPU's TLB (a mapping from virtual address
- * @vaddr to physical address @paddr) with the specified memory
+ * @addr to physical address @paddr) with the specified memory
* transaction attributes. This is generally called by the target CPU
* specific code after it has been called through the tlb_fill()
* entry point and performed a successful page table walk to find
* single TARGET_PAGE_SIZE region is mapped; the supplied @size is only
* used by tlb_flush_page.
*/
-void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
+void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr,
hwaddr paddr, MemTxAttrs attrs,
- int prot, int mmu_idx, target_ulong size);
+ int prot, int mmu_idx, vaddr size);
/* tlb_set_page:
*
* This function is equivalent to calling tlb_set_page_with_attrs()
* with an @attrs argument of MEMTXATTRS_UNSPECIFIED. It's provided
* as a convenience for CPUs which don't use memory transaction attributes.
*/
-void tlb_set_page(CPUState *cpu, target_ulong vaddr,
+void tlb_set_page(CPUState *cpu, vaddr addr,
hwaddr paddr, int prot,
- int mmu_idx, target_ulong size);
+ int mmu_idx, vaddr size);
#else
static inline void tlb_init(CPUState *cpu)
{
static inline void tlb_destroy(CPUState *cpu)
{
}
-static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
+static inline void tlb_flush_page(CPUState *cpu, vaddr addr)
{
}
-static inline void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr)
+static inline void tlb_flush_page_all_cpus(CPUState *src, vaddr addr)
{
}
-static inline void tlb_flush_page_all_cpus_synced(CPUState *src,
- target_ulong addr)
+static inline void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr)
{
}
static inline void tlb_flush(CPUState *cpu)
{
}
static inline void tlb_flush_page_by_mmuidx(CPUState *cpu,
- target_ulong addr, uint16_t idxmap)
+ vaddr addr, uint16_t idxmap)
{
}
{
}
static inline void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap)
{
}
static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap)
{
}
{
}
static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap,
unsigned bits)
{
}
static inline void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu,
- target_ulong addr,
+ vaddr addr,
uint16_t idxmap,
unsigned bits)
{
}
static inline void
-tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, target_ulong addr,
+tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr,
uint16_t idxmap, unsigned bits)
{
}
-static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr,
- target_ulong len, uint16_t idxmap,
+static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
+ vaddr len, uint16_t idxmap,
unsigned bits)
{
}
static inline void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu,
- target_ulong addr,
- target_ulong len,
+ vaddr addr,
+ vaddr len,
uint16_t idxmap,
unsigned bits)
{
}
static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu,
- target_ulong addr,
- target_long len,
+ vaddr addr,
+ vaddr len,
uint16_t idxmap,
unsigned bits)
{
* Finally, return the host address for a page that is backed by RAM,
* or NULL if the page requires I/O.
*/
-void *probe_access(CPUArchState *env, target_ulong addr, int size,
+void *probe_access(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr);
-static inline void *probe_write(CPUArchState *env, target_ulong addr, int size,
+static inline void *probe_write(CPUArchState *env, vaddr addr, int size,
int mmu_idx, uintptr_t retaddr)
{
return probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, retaddr);
}
-static inline void *probe_read(CPUArchState *env, target_ulong addr, int size,
+static inline void *probe_read(CPUArchState *env, vaddr addr, int size,
int mmu_idx, uintptr_t retaddr)
{
return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
* Do handle clean pages, so exclude TLB_NOTDIRY from the returned flags.
* For simplicity, all "mmio-like" flags are folded to TLB_MMIO.
*/
-int probe_access_flags(CPUArchState *env, target_ulong addr, int size,
+int probe_access_flags(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr);
* and must be consumed or copied immediately, before any further
* access or changes to TLB @mmu_idx.
*/
-int probe_access_full(CPUArchState *env, target_ulong addr, int size,
+int probe_access_full(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost,
CPUTLBEntryFull **pfull, uintptr_t retaddr);
/* TranslationBlock invalidate API */
#if defined(CONFIG_USER_ONLY)
-void tb_invalidate_phys_addr(target_ulong addr);
+void tb_invalidate_phys_addr(hwaddr addr);
#else
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
#endif
*
* Note: this function can trigger an exception.
*/
-tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
+tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
void **hostp);
/**
* Note: this function can trigger an exception.
*/
static inline tb_page_addr_t get_page_addr_code(CPUArchState *env,
- target_ulong addr)
+ vaddr addr)
{
return get_page_addr_code_hostp(env, addr, NULL);
}
static inline void mmap_unlock(void) {}
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length);
-void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
+void tlb_set_dirty(CPUState *cpu, vaddr addr);
MemoryRegionSection *
address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
* "didn't specify" if necessary.
*/
unsigned int unspecified:1;
- /* ARM/AMBA: TrustZone Secure access
+ /*
+ * ARM/AMBA: TrustZone Secure access
* x86: System Management Mode access
*/
unsigned int secure:1;
+ /*
+ * ARM: ArmSecuritySpace. This partially overlaps secure, but it is
+ * easier to have both fields to assist code that does not understand
+ * ARMv9 RME, or no specific knowledge of ARM at all (e.g. pflash).
+ */
+ unsigned int space:2;
/* Memory access is usermode (unprivileged) */
unsigned int user:1;
/*
* - When too many instructions have been translated.
*/
void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc,
- const TranslatorOps *ops, DisasContextBase *db);
+ vaddr pc, void *host_pc, const TranslatorOps *ops,
+ DisasContextBase *db);
/**
* translator_use_goto_tb
* Return true if goto_tb is allowed between the current TB
* and the destination PC.
*/
-bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest);
+bool translator_use_goto_tb(DisasContextBase *db, vaddr dest);
/**
* translator_io_start
bool nvdimm_supported;
bool numa_mem_supported;
bool auto_enable_numa;
+ bool cpu_cluster_has_numa_boundary;
SMPCompatProps smp_props;
const char *default_ram_id;
MMU_DATA_LOAD = 0,
MMU_DATA_STORE = 1,
MMU_INST_FETCH = 2
+#define MMU_ACCESS_COUNT 3
} MMUAccessType;
typedef struct CPUWatchpoint CPUWatchpoint;
struct kvm_dirty_gfn *kvm_dirty_gfns;
uint32_t kvm_fetch_index;
uint64_t dirty_pages;
+ int kvm_vcpu_stats_fd;
/* Use by accel-block: CPU is executing an ioctl() */
QemuLockCnt in_ioctl_lock;
void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
int ppc_cpu_pir(PowerPCCPU *cpu);
+int ppc_cpu_tir(PowerPCCPU *cpu);
/* PowerPC hardware exceptions management helpers */
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
#define SVM_H_TPM_COMM 0xEF10
#define SVM_HCALL_MAX SVM_H_TPM_COMM
-/*
- * Register state for entering a nested guest with H_ENTER_NESTED.
- * New member must be added at the end.
- */
-struct kvmppc_hv_guest_state {
- uint64_t version; /* version of this structure layout, must be first */
- uint32_t lpid;
- uint32_t vcpu_token;
- /* These registers are hypervisor privileged (at least for writing) */
- uint64_t lpcr;
- uint64_t pcr;
- uint64_t amor;
- uint64_t dpdes;
- uint64_t hfscr;
- int64_t tb_offset;
- uint64_t dawr0;
- uint64_t dawrx0;
- uint64_t ciabr;
- uint64_t hdec_expiry;
- uint64_t purr;
- uint64_t spurr;
- uint64_t ic;
- uint64_t vtb;
- uint64_t hdar;
- uint64_t hdsisr;
- uint64_t heir;
- uint64_t asdr;
- /* These are OS privileged but need to be set late in guest entry */
- uint64_t srr0;
- uint64_t srr1;
- uint64_t sprg[4];
- uint64_t pidr;
- uint64_t cfar;
- uint64_t ppr;
- /* Version 1 ends here */
- uint64_t dawr1;
- uint64_t dawrx1;
- /* Version 2 ends here */
-};
-
-/* Latest version of hv_guest_state structure */
-#define HV_GUEST_STATE_VERSION 2
-
-/* Linux 64-bit powerpc pt_regs struct, used by nested HV */
-struct kvmppc_pt_regs {
- uint64_t gpr[32];
- uint64_t nip;
- uint64_t msr;
- uint64_t orig_gpr3; /* Used for restarting system calls */
- uint64_t ctr;
- uint64_t link;
- uint64_t xer;
- uint64_t ccr;
- uint64_t softe; /* Soft enabled/disabled */
- uint64_t trap; /* Reason for being here */
- uint64_t dar; /* Fault registers */
- uint64_t dsisr; /* on 4xx/Book-E used for ESR */
- uint64_t result; /* Result of a system call */
-};
-
typedef struct SpaprDeviceTreeUpdateHeader {
uint32_t version_id;
} SpaprDeviceTreeUpdateHeader;
target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
target_ulong *args);
-void spapr_exit_nested(PowerPCCPU *cpu, int excp);
-
target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong shift);
target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong r1, target_ulong r3,
target_ulong r4);
+struct nested_ppc_state;
+
typedef struct SpaprCpuState {
uint64_t vpa_addr;
uint64_t slb_shadow_addr, slb_shadow_size;
/* Fields for nested-HV support */
bool in_nested; /* true while the L2 is executing */
- CPUPPCState *nested_host_state; /* holds the L1 state while L2 executes */
- int64_t nested_tb_offset; /* L1->L2 TB offset */
+ struct nested_ppc_state *nested_host_state; /* holds the L1 state while L2 executes */
} SpaprCpuState;
static inline SpaprCpuState *spapr_cpu_state(PowerPCCPU *cpu)
--- /dev/null
+#ifndef HW_SPAPR_NESTED_H
+#define HW_SPAPR_NESTED_H
+
+#include "qemu/osdep.h"
+#include "target/ppc/cpu.h"
+
+/*
+ * Register state for entering a nested guest with H_ENTER_NESTED.
+ * New member must be added at the end.
+ */
+struct kvmppc_hv_guest_state {
+ uint64_t version; /* version of this structure layout, must be first */
+ uint32_t lpid;
+ uint32_t vcpu_token;
+ /* These registers are hypervisor privileged (at least for writing) */
+ uint64_t lpcr;
+ uint64_t pcr;
+ uint64_t amor;
+ uint64_t dpdes;
+ uint64_t hfscr;
+ int64_t tb_offset;
+ uint64_t dawr0;
+ uint64_t dawrx0;
+ uint64_t ciabr;
+ uint64_t hdec_expiry;
+ uint64_t purr;
+ uint64_t spurr;
+ uint64_t ic;
+ uint64_t vtb;
+ uint64_t hdar;
+ uint64_t hdsisr;
+ uint64_t heir;
+ uint64_t asdr;
+ /* These are OS privileged but need to be set late in guest entry */
+ uint64_t srr0;
+ uint64_t srr1;
+ uint64_t sprg[4];
+ uint64_t pidr;
+ uint64_t cfar;
+ uint64_t ppr;
+ /* Version 1 ends here */
+ uint64_t dawr1;
+ uint64_t dawrx1;
+ /* Version 2 ends here */
+};
+
+/* Latest version of hv_guest_state structure */
+#define HV_GUEST_STATE_VERSION 2
+
+/* Linux 64-bit powerpc pt_regs struct, used by nested HV */
+struct kvmppc_pt_regs {
+ uint64_t gpr[32];
+ uint64_t nip;
+ uint64_t msr;
+ uint64_t orig_gpr3; /* Used for restarting system calls */
+ uint64_t ctr;
+ uint64_t link;
+ uint64_t xer;
+ uint64_t ccr;
+ uint64_t softe; /* Soft enabled/disabled */
+ uint64_t trap; /* Reason for being here */
+ uint64_t dar; /* Fault registers */
+ uint64_t dsisr; /* on 4xx/Book-E used for ESR */
+ uint64_t result; /* Result of a system call */
+};
+
+/*
+ * nested_ppc_state is used to save the host CPU state before switching it to
+ * the guest CPU state, to be restored on H_ENTER_NESTED exit.
+ */
+struct nested_ppc_state {
+ uint64_t gpr[32];
+ uint64_t lr;
+ uint64_t ctr;
+ uint64_t cfar;
+ uint64_t msr;
+ uint64_t nip;
+ uint32_t cr;
+
+ uint64_t xer;
+
+ uint64_t lpcr;
+ uint64_t lpidr;
+ uint64_t pidr;
+ uint64_t pcr;
+ uint64_t dpdes;
+ uint64_t hfscr;
+ uint64_t srr0;
+ uint64_t srr1;
+ uint64_t sprg0;
+ uint64_t sprg1;
+ uint64_t sprg2;
+ uint64_t sprg3;
+ uint64_t ppr;
+
+ int64_t tb_offset;
+};
+
+void spapr_register_nested(void);
+void spapr_exit_nested(PowerPCCPU *cpu, int excp);
+
+#endif /* HW_SPAPR_NESTED_H */
DECLARE_CLASS_CHECKERS(XivePresenterClass, XIVE_PRESENTER,
TYPE_XIVE_PRESENTER)
+#define XIVE_PRESENTER_GEN1_TIMA_OS 0x1
+
struct XivePresenterClass {
InterfaceClass parent;
int (*match_nvt)(XivePresenter *xptr, uint8_t format,
bool cam_ignore, uint8_t priority,
uint32_t logic_serv, XiveTCTXMatch *match);
bool (*in_kernel)(const XivePresenter *xptr);
+ uint32_t (*get_config)(XivePresenter *xptr);
};
int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
unsigned int iov_cnt;
uint32_t scanout_bitmask;
pixman_image_t *image;
+#ifdef WIN32
+ HANDLE handle;
+#endif
uint64_t hostmem;
uint64_t blob_size;
#define QEMU_DISABLE_CFI
#endif
+/*
+ * Apple clang version 14 has a bug in its __builtin_subcll(); define
+ * BUILTIN_SUBCLL_BROKEN for the offending versions so we can avoid it.
+ * When a version of Apple clang which has this bug fixed is released
+ * we can add an upper bound to this check.
+ * See https://gitlab.com/qemu-project/qemu/-/issues/1631
+ * and https://gitlab.com/qemu-project/qemu/-/issues/1659 for details.
+ * The bug never made it into any upstream LLVM releases, only Apple ones.
+ */
+#if defined(__apple_build_version__) && __clang_major__ >= 14
+#define BUILTIN_SUBCLL_BROKEN
+#endif
+
#endif /* COMPILER_H */
*/
static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow)
{
-#if __has_builtin(__builtin_subcll)
+#if __has_builtin(__builtin_subcll) && !defined(BUILTIN_SUBCLL_BROKEN)
unsigned long long b = *pborrow;
x = __builtin_subcll(x, y, b, &b);
*pborrow = b & 1;
* It would only fail if not called from an instrumented memory access
* which would be an abuse of the API.
*/
-bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx,
+bool tlb_plugin_lookup(CPUState *cpu, vaddr addr, int mmu_idx,
bool is_store, struct qemu_plugin_hwaddr *data);
#endif /* PLUGIN_MEMORY_H */
}
#endif
-#ifdef CONFIG_PROFILER
-static inline int64_t profile_getclock(void)
-{
- return get_clock();
-}
-
-extern int64_t dev_time;
-#endif
-
#endif
win32_close_exception_handler(struct _EXCEPTION_RECORD*, void*,
struct _CONTEXT*, void*);
+void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp);
+void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp);
+
#ifdef __cplusplus
}
#endif
return i < ARRAY_SIZE(op->output_pref) ? op->output_pref[i] : 0;
}
-typedef struct TCGProfile {
- int64_t cpu_exec_time;
- int64_t tb_count1;
- int64_t tb_count;
- int64_t op_count; /* total insn count */
- int op_count_max; /* max insn per TB */
- int temp_count_max;
- int64_t temp_count;
- int64_t del_op_count;
- int64_t code_in_len;
- int64_t code_out_len;
- int64_t search_out_len;
- int64_t interm_time;
- int64_t code_time;
- int64_t la_time;
- int64_t opt_time;
- int64_t restore_count;
- int64_t restore_time;
- int64_t table_op_count[NB_OPS];
-} TCGProfile;
-
struct TCGContext {
uint8_t *pool_cur, *pool_end;
TCGPool *pool_first, *pool_current, *pool_first_large;
tcg_insn_unit *code_buf; /* pointer for start of tb */
tcg_insn_unit *code_ptr; /* pointer for running end of tb */
-#ifdef CONFIG_PROFILER
- TCGProfile prof;
-#endif
-
#ifdef CONFIG_DEBUG_TCG
int goto_tb_issue_mask;
const TCGOpcode *vecop_list;
return temp_tcgv_ptr(t);
}
-int64_t tcg_cpu_exec_time(void);
void tcg_dump_info(GString *buf);
void tcg_dump_op_count(GString *buf);
#include "qom/object.h"
#include "qemu/notify.h"
#include "qapi/qapi-types-ui.h"
+#include "ui/input.h"
#ifdef CONFIG_OPENGL
# include <epoxy/gl.h>
void kbd_put_string_console(QemuConsole *s, const char *str, int len);
void kbd_put_keysym(int keysym);
+/* Touch devices */
+typedef struct touch_slot {
+ int x;
+ int y;
+ int tracking_id;
+} touch_slot;
+
+void console_handle_touch_event(QemuConsole *con,
+ struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
+ uint64_t num_slot,
+ int width, int height,
+ double x, double y,
+ InputMultiTouchType type,
+ Error **errp);
/* consoles */
#define TYPE_QEMU_CONSOLE "qemu-console"
uint32_t y;
uint32_t width;
uint32_t height;
+ void *d3d_tex2d;
} ScanoutTexture;
typedef struct DisplaySurface {
GLenum gltype;
GLuint texture;
#endif
+#ifdef WIN32
+ HANDLE handle;
+ uint32_t handle_offset;
+#endif
} DisplaySurface;
typedef struct QemuUIInfo {
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
/* optional (default to true if has dpy_gl_scanout_dmabuf) */
bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl);
/* optional */
DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image);
DisplaySurface *qemu_create_placeholder_surface(int w, int h,
const char *msg);
+#ifdef WIN32
+void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
+ HANDLE h, uint32_t offset);
+#endif
PixelFormat qemu_default_pixelformat(int bpp);
DisplaySurface *qemu_create_displaysurface(int width, int height);
void dpy_gl_scanout_texture(QemuConsole *con,
uint32_t backing_id, bool backing_y_0_top,
uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void dpy_gl_scanout_dmabuf(QemuConsole *con,
QemuDmaBuf *dmabuf);
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
extern EGLDisplay *qemu_egl_display;
extern EGLConfig qemu_egl_config;
extern DisplayGLMode qemu_egl_mode;
+extern bool qemu_egl_angle_d3d;
typedef struct egl_fb {
int width;
void egl_fb_setup_new_tex(egl_fb *fb, int width, int height);
void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip);
void egl_fb_read(DisplaySurface *dst, egl_fb *src);
+void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h);
void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip);
void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
int x, int y, double scale_x, double scale_y);
+extern EGLContext qemu_egl_rn_ctx;
+
#ifdef CONFIG_GBM
extern int qemu_egl_rn_fd;
extern struct gbm_device *qemu_egl_rn_gbm_dev;
-extern EGLContext qemu_egl_rn_ctx;
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode);
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
#endif
+#ifdef WIN32
+int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode);
+#endif
+
EGLContext qemu_egl_init_ctx(void);
bool qemu_egl_has_dmabuf(void);
bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp);
+const char *qemu_egl_get_error_string(void);
+
#endif /* EGL_HELPERS_H */
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
switch(trapnr) {
case 0x80:
+#ifndef TARGET_X86_64
+ case EXCP_SYSCALL:
+#endif
/* linux syscall from int $0x80 */
ret = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EAX] = ret;
}
break;
-#ifndef TARGET_ABI32
+#ifdef TARGET_X86_64
case EXCP_SYSCALL:
- /* linux syscall from syscall instruction */
+ /* linux syscall from syscall instruction. */
ret = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_EAX] = ret;
}
break;
-#endif
-#ifdef TARGET_X86_64
case EXCP_VSYSCALL:
emulate_vsyscall(env);
break;
gdbus_codegen_error = '@0@ uses gdbus-codegen, which does not support control flow integrity'
endif
+xml_pp = find_program('scripts/xml-preprocess.py')
+
lttng = not_found
if 'ust' in get_option('trace_backends')
lttng = dependency('lttng-ust', required: true, version: '>= 2.1',
virgl = dependency('virglrenderer',
method: 'pkg-config',
required: get_option('virglrenderer'))
+ if virgl.found()
+ config_host_data.set('HAVE_VIRGL_D3D_INFO_EXT',
+ cc.has_member('struct virgl_renderer_resource_info_ext', 'd3d_tex2d',
+ prefix: '#include <virglrenderer.h>',
+ dependencies: virgl))
+ endif
endif
blkio = not_found
if not get_option('blkio').auto() or have_block
error_message: '-display dbus requires glib>=2.64') \
.require(gdbus_codegen.found(),
error_message: gdbus_codegen_error.format('-display dbus')) \
- .require(targetos != 'windows',
- error_message: '-display dbus is not available on Windows') \
.allowed()
have_virtfs = get_option('virtfs') \
dependencies: numa))
endif
config_host_data.set('CONFIG_OPENGL', opengl.found())
-config_host_data.set('CONFIG_PROFILER', get_option('profiler'))
config_host_data.set('CONFIG_RBD', rbd.found())
config_host_data.set('CONFIG_RDMA', rdma.found())
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
config_host_data.set('CONFIG_DUP3', cc.has_function('dup3'))
config_host_data.set('CONFIG_FALLOCATE', cc.has_function('fallocate'))
config_host_data.set('CONFIG_POSIX_FALLOCATE', cc.has_function('posix_fallocate'))
+config_host_data.set('CONFIG_GETCPU', cc.has_function('getcpu', prefix: gnu_source_prefix))
+config_host_data.set('CONFIG_SCHED_GETCPU', cc.has_function('sched_getcpu', prefix: '#include <sched.h>'))
# Note that we need to specify prefix: here to avoid incorrectly
# thinking that Windows has posix_memalign()
config_host_data.set('CONFIG_POSIX_MEMALIGN', cc.has_function('posix_memalign', prefix: '#include <stdlib.h>'))
summary_info += {'QEMU_OBJCFLAGS': ' '.join(qemu_common_flags)}
endif
summary_info += {'QEMU_LDFLAGS': ' '.join(qemu_ldflags)}
-summary_info += {'profiler': get_option('profiler')}
summary_info += {'link-time optimization (LTO)': get_option('b_lto')}
summary_info += {'PIE': get_option('b_pie')}
summary_info += {'static build': get_option('prefer_static')}
option('gprof', type: 'boolean', value: false,
description: 'QEMU profiling with gprof',
deprecated: true)
-option('profiler', type: 'boolean', value: false,
- description: 'profiler support')
option('slirp_smbd', type : 'feature', value : 'auto',
description: 'use smbd (at path --smbd=*) in slirp networking')
keymaps = {
- 'ar': '-l ar',
+ 'ar': '-l ara',
'bepo': '-l fr -v dvorak',
'cz': '-l cz',
'da': '-l dk',
'if': 'CONFIG_TCG',
'features': [ 'unstable' ] }
-##
-# @x-query-profile:
-#
-# Query TCG profiling information
-#
-# Features:
-#
-# @unstable: This command is meant for debugging.
-#
-# Returns: profile information
-#
-# Since: 6.2
-##
-{ 'command': 'x-query-profile',
- 'returns': 'HumanReadableText',
- 'if': 'CONFIG_TCG',
- 'features': [ 'unstable' ] }
-
##
# @x-query-ramblock:
#
{ 'name': 'none' },
{ 'name': 'gtk', 'if': 'CONFIG_GTK' },
{ 'name': 'sdl', 'if': 'CONFIG_SDL' },
- { 'name': 'egl-headless',
- 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
+ { 'name': 'egl-headless', 'if': 'CONFIG_OPENGL' },
{ 'name': 'curses', 'if': 'CONFIG_CURSES' },
{ 'name': 'cocoa', 'if': 'CONFIG_COCOA' },
{ 'name': 'spice-app', 'if': 'CONFIG_SPICE' },
'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' },
'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' },
'egl-headless': { 'type': 'DisplayEGLHeadless',
- 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
+ 'if': 'CONFIG_OPENGL' },
'dbus': { 'type': 'DisplayDBus', 'if': 'CONFIG_DBUS_DISPLAY' },
'sdl': { 'type': 'DisplaySDL', 'if': 'CONFIG_SDL' }
}
-Subproject commit 1cfbe76ff625fce9ed5991f7e13d80ffec900f40
+Subproject commit 673d2595d4f773cc266cbf8dbaf2f475a6adb949
shift
maybe_modules="$@"
-# if not running in a git checkout, do nothing
-test "$command" = "ignore" && exit 0
-
+test -z "$maybe_modules" && exit 0
test -z "$GIT" && GIT=$(command -v git)
cd "$(dirname "$0")/.."
+no_git_error=
+if ! test -e ".git"; then
+ no_git_error='no git checkout exists'
+elif test -z "$GIT"; then
+ no_git_error='git binary not found'
+fi
+
+is_git() {
+ test -z "$no_git_error"
+}
+
update_error() {
echo "$0: $*"
echo
}
validate_error() {
- if test "$1" = "validate"; then
+ if is_git && test "$1" = "validate"; then
echo "GIT submodules checkout is out of date, and submodules"
echo "configured for validate only. Please run"
echo " scripts/git-submodule.sh update $maybe_modules"
test "$CURSTATUS" = "$OLDSTATUS"
}
-if test -n "$maybe_modules" && ! test -e ".git"
-then
- echo "$0: unexpectedly called with submodules but no git checkout exists"
- exit 1
-fi
-
-if test -n "$maybe_modules" && test -z "$GIT"
-then
- echo "$0: unexpectedly called with submodules but git binary not found"
- exit 1
+if is_git; then
+ test -e $substat || touch $substat
+ modules=""
+ for m in $maybe_modules
+ do
+ $GIT submodule status $m 1> /dev/null 2>&1
+ if test $? = 0
+ then
+ modules="$modules $m"
+ grep $m $substat > /dev/null 2>&1 || $GIT submodule status $module >> $substat
+ else
+ echo "warn: ignoring non-existent submodule $m"
+ fi
+ done
+else
+ modules=$maybe_modules
fi
-modules=""
-for m in $maybe_modules
-do
- $GIT submodule status $m 1> /dev/null 2>&1
- if test $? = 0
- then
- modules="$modules $m"
- else
- echo "warn: ignoring non-existent submodule $m"
- fi
-done
-
case "$command" in
status|validate)
- test -f "$substat" || validate_error "$command"
- test -z "$maybe_modules" && exit 0
for module in $modules; do
- check_updated $module || validate_error "$command"
+ if is_git; then
+ check_updated $module || validate_error "$command"
+ elif ! (set xyz "$module"/* && test -e "$2"); then
+ # The directory does not exist or it contains no files
+ echo "$0: sources not available for $module and $no_git_error"
+ validate_error "$command"
+ fi
done
- exit 0
;;
+
update)
- test -e $substat || touch $substat
- test -z "$maybe_modules" && exit 0
+ is_git || {
+ echo "$0: unexpectedly called with submodules but $no_git_error"
+ exit 1
+ }
$GIT submodule update --init $modules 1>/dev/null
test $? -ne 0 && update_error "failed to update modules"
printf "%s\n" ' jemalloc/system/tcmalloc)'
printf "%s\n" ' --enable-module-upgrades try to load modules from alternate paths for'
printf "%s\n" ' upgrades'
- printf "%s\n" ' --enable-profiler profiler support'
printf "%s\n" ' --enable-rng-none dummy RNG, avoid using /dev/(u)random and'
printf "%s\n" ' getrandom()'
printf "%s\n" ' --enable-safe-stack SafeStack Stack Smash Protection (requires'
--with-pkgversion=*) quote_sh "-Dpkgversion=$2" ;;
--enable-png) printf "%s" -Dpng=enabled ;;
--disable-png) printf "%s" -Dpng=disabled ;;
- --enable-profiler) printf "%s" -Dprofiler=true ;;
- --disable-profiler) printf "%s" -Dprofiler=false ;;
--enable-pvrdma) printf "%s" -Dpvrdma=enabled ;;
--disable-pvrdma) printf "%s" -Dpvrdma=disabled ;;
--enable-qcow1) printf "%s" -Dqcow1=enabled ;;
if stap.found()
install_data('qemu-trace-stap', install_dir: get_option('bindir'))
endif
+
+test('xml-preprocess', files('xml-preprocess-test.py'), suite: ['unit'])
--- /dev/null
+#!/usr/bin/env python3
+#
+# Copyright (c) 2023 Red Hat, Inc.
+#
+# SPDX-License-Identifier: MIT
+"""Unit tests for xml-preprocess"""
+
+import contextlib
+import importlib
+import os
+import platform
+import subprocess
+import tempfile
+import unittest
+from io import StringIO
+
+xmlpp = importlib.import_module("xml-preprocess")
+
+
+class TestXmlPreprocess(unittest.TestCase):
+ """Tests for xml-preprocess.Preprocessor"""
+
+ def test_preprocess_xml(self):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file:
+ temp_file.write("<root></root>")
+ temp_file_name = temp_file.name
+ result = xmlpp.preprocess_xml(temp_file_name)
+ self.assertEqual(result, "<root></root>")
+ os.remove(temp_file_name)
+
+ def test_save_xml(self):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file:
+ temp_file_name = temp_file.name
+ xmlpp.save_xml("<root></root>", temp_file_name)
+ self.assertTrue(os.path.isfile(temp_file_name))
+ os.remove(temp_file_name)
+
+ def test_include(self):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as inc_file:
+ inc_file.write("<included>Content from included file</included>")
+ inc_file_name = inc_file.name
+ xml_str = f"<?include {inc_file_name} ?>"
+ expected = "<included>Content from included file</included>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ os.remove(inc_file_name)
+ self.assertRaises(FileNotFoundError, xpp.preprocess, xml_str)
+
+ def test_envvar(self):
+ os.environ["TEST_ENV_VAR"] = "TestValue"
+ xml_str = "<root>$(env.TEST_ENV_VAR)</root>"
+ expected = "<root>TestValue</root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ self.assertRaises(KeyError, xpp.preprocess, "$(env.UNKNOWN)")
+
+ def test_sys_var(self):
+ xml_str = "<root>$(sys.ARCH)</root>"
+ expected = f"<root>{platform.architecture()[0]}</root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ self.assertRaises(KeyError, xpp.preprocess, "$(sys.UNKNOWN)")
+
+ def test_cus_var(self):
+ xml_str = "<root>$(var.USER)</root>"
+ expected = "<root></root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ xml_str = "<?define USER=FOO?><root>$(var.USER)</root>"
+ expected = "<root>FOO</root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+
+ def test_error_warning(self):
+ xml_str = "<root><?warning \"test warn\"?></root>"
+ expected = "<root></root>"
+ xpp = xmlpp.Preprocessor()
+ out = StringIO()
+ with contextlib.redirect_stdout(out):
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ self.assertEqual(out.getvalue(), "[Warning]: test warn\n")
+ self.assertRaises(RuntimeError, xpp.preprocess, "<?error \"test\"?>")
+
+ def test_cmd(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess('<root><?cmd "echo hello world"?></root>')
+ self.assertEqual(result, "<root>hello world</root>")
+ self.assertRaises(
+ subprocess.CalledProcessError,
+ xpp.preprocess, '<?cmd "test-unknown-cmd"?>'
+ )
+
+ def test_foreach(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(
+ '<root><?foreach x in a;b;c?>$(var.x)<?endforeach?></root>'
+ )
+ self.assertEqual(result, "<root>abc</root>")
+
+ def test_if_elseif(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess('<root><?if True?>ok<?endif?></root>')
+ self.assertEqual(result, "<root>ok</root>")
+ result = xpp.preprocess('<root><?if False?>ok<?endif?></root>')
+ self.assertEqual(result, "<root></root>")
+ result = xpp.preprocess('<root><?if True?>ok<?else?>ko<?endif?></root>')
+ self.assertEqual(result, "<root>ok</root>")
+ result = xpp.preprocess('<root><?if False?>ok<?else?>ko<?endif?></root>')
+ self.assertEqual(result, "<root>ko</root>")
+ result = xpp.preprocess(
+ '<root><?if False?>ok<?elseif True?>ok2<?else?>ko<?endif?></root>'
+ )
+ self.assertEqual(result, "<root>ok2</root>")
+ result = xpp.preprocess(
+ '<root><?if False?>ok<?elseif False?>ok<?else?>ko<?endif?></root>'
+ )
+ self.assertEqual(result, "<root>ko</root>")
+
+ def test_ifdef(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess('<root><?ifdef USER?>ok<?else?>ko<?endif?></root>')
+ self.assertEqual(result, "<root>ko</root>")
+ result = xpp.preprocess(
+ '<?define USER=FOO?><root><?ifdef USER?>ok<?else?>ko<?endif?></root>'
+ )
+ self.assertEqual(result, "<root>ok</root>")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null
+#!/usr/bin/env python3
+#
+# Copyright (c) 2017-2019 Tony Su
+# Copyright (c) 2023 Red Hat, Inc.
+#
+# SPDX-License-Identifier: MIT
+#
+# Adapted from https://github.com/peitaosu/XML-Preprocessor
+#
+"""This is a XML Preprocessor which can be used to process your XML file before
+you use it, to process conditional statements, variables, iteration
+statements, error/warning, execute command, etc.
+
+## XML Schema
+
+### Include Files
+```
+<?include path/to/file ?>
+```
+
+### Variables
+```
+$(env.EnvironmentVariable)
+
+$(sys.SystemVariable)
+
+$(var.CustomVariable)
+```
+
+### Conditional Statements
+```
+<?if ?>
+
+<?ifdef ?>
+
+<?ifndef ?>
+
+<?else?>
+
+<?elseif ?>
+
+<?endif?>
+```
+
+### Iteration Statements
+```
+<?foreach VARNAME in 1;2;3?>
+ $(var.VARNAME)
+<?endforeach?>
+```
+
+### Errors and Warnings
+```
+<?error "This is error message!" ?>
+
+<?warning "This is warning message!" ?>
+```
+
+### Commands
+```
+<? cmd "echo hello world" ?>
+```
+"""
+
+import os
+import platform
+import re
+import subprocess
+import sys
+from typing import Optional
+from xml.dom import minidom
+
+
+class Preprocessor():
+ """This class holds the XML preprocessing state"""
+
+ def __init__(self):
+ self.sys_vars = {
+ "ARCH": platform.architecture()[0],
+ "SOURCE": os.path.abspath(__file__),
+ "CURRENT": os.getcwd(),
+ }
+ self.cus_vars = {}
+
+ def _pp_include(self, xml_str: str) -> str:
+ include_regex = r"(<\?include([\w\s\\/.:_-]+)\s*\?>)"
+ matches = re.findall(include_regex, xml_str)
+ for group_inc, group_xml in matches:
+ inc_file_path = group_xml.strip()
+ with open(inc_file_path, "r", encoding="utf-8") as inc_file:
+ inc_file_content = inc_file.read()
+ xml_str = xml_str.replace(group_inc, inc_file_content)
+ return xml_str
+
+ def _pp_env_var(self, xml_str: str) -> str:
+ envvar_regex = r"(\$\(env\.(\w+)\))"
+ matches = re.findall(envvar_regex, xml_str)
+ for group_env, group_var in matches:
+ xml_str = xml_str.replace(group_env, os.environ[group_var])
+ return xml_str
+
+ def _pp_sys_var(self, xml_str: str) -> str:
+ sysvar_regex = r"(\$\(sys\.(\w+)\))"
+ matches = re.findall(sysvar_regex, xml_str)
+ for group_sys, group_var in matches:
+ xml_str = xml_str.replace(group_sys, self.sys_vars[group_var])
+ return xml_str
+
+ def _pp_cus_var(self, xml_str: str) -> str:
+ define_regex = r"(<\?define\s*(\w+)\s*=\s*([\w\s\"]+)\s*\?>)"
+ matches = re.findall(define_regex, xml_str)
+ for group_def, group_name, group_var in matches:
+ group_name = group_name.strip()
+ group_var = group_var.strip().strip("\"")
+ self.cus_vars[group_name] = group_var
+ xml_str = xml_str.replace(group_def, "")
+ cusvar_regex = r"(\$\(var\.(\w+)\))"
+ matches = re.findall(cusvar_regex, xml_str)
+ for group_cus, group_var in matches:
+ xml_str = xml_str.replace(
+ group_cus,
+ self.cus_vars.get(group_var, "")
+ )
+ return xml_str
+
+ def _pp_foreach(self, xml_str: str) -> str:
+ foreach_regex = r"(<\?foreach\s+(\w+)\s+in\s+([\w;]+)\s*\?>(.*)<\?endforeach\?>)"
+ matches = re.findall(foreach_regex, xml_str)
+ for group_for, group_name, group_vars, group_text in matches:
+ group_texts = ""
+ for var in group_vars.split(";"):
+ self.cus_vars[group_name] = var
+ group_texts += self._pp_cus_var(group_text)
+ xml_str = xml_str.replace(group_for, group_texts)
+ return xml_str
+
+ def _pp_error_warning(self, xml_str: str) -> str:
+ error_regex = r"<\?error\s*\"([^\"]+)\"\s*\?>"
+ matches = re.findall(error_regex, xml_str)
+ for group_var in matches:
+ raise RuntimeError("[Error]: " + group_var)
+ warning_regex = r"(<\?warning\s*\"([^\"]+)\"\s*\?>)"
+ matches = re.findall(warning_regex, xml_str)
+ for group_wrn, group_var in matches:
+ print("[Warning]: " + group_var)
+ xml_str = xml_str.replace(group_wrn, "")
+ return xml_str
+
+ def _pp_if_eval(self, xml_str: str) -> str:
+ ifelif_regex = (
+ r"(<\?(if|elseif)\s*([^\"\s=<>!]+)\s*([!=<>]+)\s*\"*([^\"=<>!]+)\"*\s*\?>)"
+ )
+ matches = re.findall(ifelif_regex, xml_str)
+ for ifelif, tag, left, operator, right in matches:
+ if "<" in operator or ">" in operator:
+ result = eval(f"{left} {operator} {right}")
+ else:
+ result = eval(f'"{left}" {operator} "{right}"')
+ xml_str = xml_str.replace(ifelif, f"<?{tag} {result}?>")
+ return xml_str
+
+ def _pp_ifdef_ifndef(self, xml_str: str) -> str:
+ ifndef_regex = r"(<\?(ifdef|ifndef)\s*([\w]+)\s*\?>)"
+ matches = re.findall(ifndef_regex, xml_str)
+ for group_ifndef, group_tag, group_var in matches:
+ if group_tag == "ifdef":
+ result = group_var in self.cus_vars
+ else:
+ result = group_var not in self.cus_vars
+ xml_str = xml_str.replace(group_ifndef, f"<?if {result}?>")
+ return xml_str
+
+ def _pp_if_elseif(self, xml_str: str) -> str:
+ if_elif_else_regex = (
+ r"(<\?if\s(True|False)\?>"
+ r"(.*?)"
+ r"<\?elseif\s(True|False)\?>"
+ r"(.*?)"
+ r"<\?else\?>"
+ r"(.*?)"
+ r"<\?endif\?>)"
+ )
+ if_else_regex = (
+ r"(<\?if\s(True|False)\?>"
+ r"(.*?)"
+ r"<\?else\?>"
+ r"(.*?)"
+ r"<\?endif\?>)"
+ )
+ if_regex = r"(<\?if\s(True|False)\?>(.*?)<\?endif\?>)"
+ matches = re.findall(if_elif_else_regex, xml_str, re.DOTALL)
+ for (group_full, group_if, group_if_elif, group_elif,
+ group_elif_else, group_else) in matches:
+ result = ""
+ if group_if == "True":
+ result = group_if_elif
+ elif group_elif == "True":
+ result = group_elif_else
+ else:
+ result = group_else
+ xml_str = xml_str.replace(group_full, result)
+ matches = re.findall(if_else_regex, xml_str, re.DOTALL)
+ for group_full, group_if, group_if_else, group_else in matches:
+ result = ""
+ if group_if == "True":
+ result = group_if_else
+ else:
+ result = group_else
+ xml_str = xml_str.replace(group_full, result)
+ matches = re.findall(if_regex, xml_str, re.DOTALL)
+ for group_full, group_if, group_text in matches:
+ result = ""
+ if group_if == "True":
+ result = group_text
+ xml_str = xml_str.replace(group_full, result)
+ return xml_str
+
+ def _pp_command(self, xml_str: str) -> str:
+ cmd_regex = r"(<\?cmd\s*\"([^\"]+)\"\s*\?>)"
+ matches = re.findall(cmd_regex, xml_str)
+ for group_cmd, group_exec in matches:
+ output = subprocess.check_output(
+ group_exec, shell=True,
+ text=True, stderr=subprocess.STDOUT
+ )
+ xml_str = xml_str.replace(group_cmd, output)
+ return xml_str
+
+ def _pp_blanks(self, xml_str: str) -> str:
+ right_blank_regex = r">[\n\s\t\r]*"
+ left_blank_regex = r"[\n\s\t\r]*<"
+ xml_str = re.sub(right_blank_regex, ">", xml_str)
+ xml_str = re.sub(left_blank_regex, "<", xml_str)
+ return xml_str
+
+ def preprocess(self, xml_str: str) -> str:
+ fns = [
+ self._pp_blanks,
+ self._pp_include,
+ self._pp_foreach,
+ self._pp_env_var,
+ self._pp_sys_var,
+ self._pp_cus_var,
+ self._pp_if_eval,
+ self._pp_ifdef_ifndef,
+ self._pp_if_elseif,
+ self._pp_command,
+ self._pp_error_warning,
+ ]
+
+ while True:
+ changed = False
+ for func in fns:
+ out_xml = func(xml_str)
+ if not changed and out_xml != xml_str:
+ changed = True
+ xml_str = out_xml
+ if not changed:
+ break
+
+ return xml_str
+
+
+def preprocess_xml(path: str) -> str:
+ with open(path, "r", encoding="utf-8") as original_file:
+ input_xml = original_file.read()
+
+ proc = Preprocessor()
+ return proc.preprocess(input_xml)
+
+
+def save_xml(xml_str: str, path: Optional[str]):
+ xml = minidom.parseString(xml_str)
+ with open(path, "w", encoding="utf-8") if path else sys.stdout as output_file:
+ output_file.write(xml.toprettyxml())
+
+
+def main():
+ if len(sys.argv) < 2:
+ print("Usage: xml-preprocessor input.xml [output.xml]")
+ sys.exit(1)
+
+ output_file = None
+ if len(sys.argv) == 3:
+ output_file = sys.argv[2]
+
+ input_file = sys.argv[1]
+ output_xml = preprocess_xml(input_file)
+ save_xml(output_xml, output_file)
+
+
+if __name__ == "__main__":
+ main()
int qemu_main_loop(void)
{
int status = EXIT_SUCCESS;
-#ifdef CONFIG_PROFILER
- int64_t ti;
-#endif
while (!main_loop_should_exit(&status)) {
-#ifdef CONFIG_PROFILER
- ti = profile_getclock();
-#endif
main_loop_wait(false);
-#ifdef CONFIG_PROFILER
- dev_time += profile_getclock() - ti;
-#endif
}
return status;
MemTxResult response, uintptr_t retaddr);
#endif
-static inline void cpu_get_tb_cpu_state(CPUAlphaState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *pflags)
+static inline void cpu_get_tb_cpu_state(CPUAlphaState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags)
{
*pc = env->pc;
*cs_base = 0;
* KVM does not currently allow us to lie to the guest about its
* ID/feature registers, so the guest always sees what the host has.
*/
- if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)
- ? cpu_isar_feature(aa64_fp_simd, cpu)
- : cpu_isar_feature(aa32_vfp, cpu)) {
- cpu->has_vfp = true;
- if (!kvm_enabled()) {
- qdev_property_add_static(DEVICE(obj), &arm_cpu_has_vfp_property);
+ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ if (cpu_isar_feature(aa64_fp_simd, cpu)) {
+ cpu->has_vfp = true;
+ cpu->has_vfp_d32 = true;
+ if (tcg_enabled() || qtest_enabled()) {
+ qdev_property_add_static(DEVICE(obj),
+ &arm_cpu_has_vfp_property);
+ }
}
- }
-
- if (cpu->has_vfp && cpu_isar_feature(aa32_simd_r32, cpu)) {
- cpu->has_vfp_d32 = true;
- if (!kvm_enabled()) {
+ } else if (cpu_isar_feature(aa32_vfp, cpu)) {
+ cpu->has_vfp = true;
+ if (cpu_isar_feature(aa32_simd_r32, cpu)) {
+ cpu->has_vfp_d32 = true;
/*
* The permitted values of the SIMDReg bits [3:0] on
* Armv8-A are either 0b0000 and 0b0010. On such CPUs,
* make sure that has_vfp_d32 can not be set to false.
*/
- if (!(arm_feature(&cpu->env, ARM_FEATURE_V8) &&
- !arm_feature(&cpu->env, ARM_FEATURE_M))) {
+ if ((tcg_enabled() || qtest_enabled())
+ && !(arm_feature(&cpu->env, ARM_FEATURE_V8)
+ && !arm_feature(&cpu->env, ARM_FEATURE_M))) {
qdev_property_add_static(DEVICE(obj),
&arm_cpu_has_vfp_d32_property);
}
cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPSDBG, 0);
cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0,
ID_AA64PFR0, EL3, 0);
+
+ /* Disable the realm management extension, which requires EL3. */
+ cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0,
+ ID_AA64PFR0, RME, 0);
}
if (!cpu->has_el2) {
#define EXCP_UNALIGNED 22 /* v7M UNALIGNED UsageFault */
#define EXCP_DIVBYZERO 23 /* v7M DIVBYZERO UsageFault */
#define EXCP_VSERR 24
+#define EXCP_GPC 25 /* v9 Granule Protection Check Fault */
/* NB: add new EXCP_ defines to the array in arm_log_exception() too */
#define ARMV7M_EXCP_RESET 1
uint64_t fgt_read[2]; /* HFGRTR, HDFGRTR */
uint64_t fgt_write[2]; /* HFGWTR, HDFGWTR */
uint64_t fgt_exec[1]; /* HFGITR */
+
+ /* RME registers */
+ uint64_t gpccr_el3;
+ uint64_t gptbr_el3;
+ uint64_t mfar_el3;
} cp15;
struct {
uint64_t reset_cbar;
uint32_t reset_auxcr;
bool reset_hivecs;
+ uint8_t reset_l0gptsz;
/*
* Intermediate values used during property parsing.
#define HCR_TERR (1ULL << 36)
#define HCR_TEA (1ULL << 37)
#define HCR_MIOCNCE (1ULL << 38)
-/* RES0 bit 39 */
+#define HCR_TME (1ULL << 39)
#define HCR_APK (1ULL << 40)
#define HCR_API (1ULL << 41)
#define HCR_NV (1ULL << 42)
#define HCR_NV2 (1ULL << 45)
#define HCR_FWB (1ULL << 46)
#define HCR_FIEN (1ULL << 47)
-/* RES0 bit 48 */
+#define HCR_GPF (1ULL << 48)
#define HCR_TID4 (1ULL << 49)
#define HCR_TICAB (1ULL << 50)
#define HCR_AMVOFFEN (1ULL << 51)
#define SCR_TRNDR (1ULL << 40)
#define SCR_ENTP2 (1ULL << 41)
#define SCR_GPF (1ULL << 48)
+#define SCR_NSE (1ULL << 62)
#define HSTR_TTEE (1 << 16)
#define HSTR_TJDBX (1 << 17)
FIELD(ID_AA64PFR0, MPAM, 40, 4)
FIELD(ID_AA64PFR0, AMU, 44, 4)
FIELD(ID_AA64PFR0, DIT, 48, 4)
+FIELD(ID_AA64PFR0, RME, 52, 4)
FIELD(ID_AA64PFR0, CSV2, 56, 4)
FIELD(ID_AA64PFR0, CSV3, 60, 4)
FIELD(MVFR2, SIMDMISC, 0, 4)
FIELD(MVFR2, FPMISC, 4, 4)
+FIELD(GPCCR, PPS, 0, 3)
+FIELD(GPCCR, IRGN, 8, 2)
+FIELD(GPCCR, ORGN, 10, 2)
+FIELD(GPCCR, SH, 12, 2)
+FIELD(GPCCR, PGS, 14, 2)
+FIELD(GPCCR, GPC, 16, 1)
+FIELD(GPCCR, GPCP, 17, 1)
+FIELD(GPCCR, L0GPTSZ, 20, 4)
+
+FIELD(MFAR, FPA, 12, 40)
+FIELD(MFAR, NSE, 62, 1)
+FIELD(MFAR, NS, 63, 1)
+
QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK);
/* If adding a feature bit which corresponds to a Linux ELF
void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp);
-#if !defined(CONFIG_USER_ONLY)
/*
+ * ARM v9 security states.
+ * The ordering of the enumeration corresponds to the low 2 bits
+ * of the GPI value, and (except for Root) the concat of NSE:NS.
+ */
+
+typedef enum ARMSecuritySpace {
+ ARMSS_Secure = 0,
+ ARMSS_NonSecure = 1,
+ ARMSS_Root = 2,
+ ARMSS_Realm = 3,
+} ARMSecuritySpace;
+
+/* Return true if @space is secure, in the pre-v9 sense. */
+static inline bool arm_space_is_secure(ARMSecuritySpace space)
+{
+ return space == ARMSS_Secure || space == ARMSS_Root;
+}
+
+/* Return the ARMSecuritySpace for @secure, assuming !RME or EL[0-2]. */
+static inline ARMSecuritySpace arm_secure_to_space(bool secure)
+{
+ return secure ? ARMSS_Secure : ARMSS_NonSecure;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+/**
+ * arm_security_space_below_el3:
+ * @env: cpu context
+ *
+ * Return the security space of exception levels below EL3, following
+ * an exception return to those levels. Unlike arm_security_space,
+ * this doesn't care about the current EL.
+ */
+ARMSecuritySpace arm_security_space_below_el3(CPUARMState *env);
+
+/**
+ * arm_is_secure_below_el3:
+ * @env: cpu context
+ *
* Return true if exception levels below EL3 are in secure state,
- * or would be following an exception return to that level.
- * Unlike arm_is_secure() (which is always a question about the
- * _current_ state of the CPU) this doesn't care about the current
- * EL or mode.
+ * or would be following an exception return to those levels.
*/
static inline bool arm_is_secure_below_el3(CPUARMState *env)
{
- assert(!arm_feature(env, ARM_FEATURE_M));
- if (arm_feature(env, ARM_FEATURE_EL3)) {
- return !(env->cp15.scr_el3 & SCR_NS);
- } else {
- /* If EL3 is not supported then the secure state is implementation
- * defined, in which case QEMU defaults to non-secure.
- */
- return false;
- }
+ ARMSecuritySpace ss = arm_security_space_below_el3(env);
+ return ss == ARMSS_Secure;
}
/* Return true if the CPU is AArch64 EL3 or AArch32 Mon */
return false;
}
-/* Return true if the processor is in secure state */
+/**
+ * arm_security_space:
+ * @env: cpu context
+ *
+ * Return the current security space of the cpu.
+ */
+ARMSecuritySpace arm_security_space(CPUARMState *env);
+
+/**
+ * arm_is_secure:
+ * @env: cpu context
+ *
+ * Return true if the processor is in secure state.
+ */
static inline bool arm_is_secure(CPUARMState *env)
{
- if (arm_feature(env, ARM_FEATURE_M)) {
- return env->v7m.secure;
- }
- if (arm_is_el3_or_mon(env)) {
- return true;
- }
- return arm_is_secure_below_el3(env);
+ return arm_space_is_secure(arm_security_space(env));
}
/*
}
#else
+static inline ARMSecuritySpace arm_security_space_below_el3(CPUARMState *env)
+{
+ return ARMSS_NonSecure;
+}
+
static inline bool arm_is_secure_below_el3(CPUARMState *env)
{
return false;
}
+static inline ARMSecuritySpace arm_security_space(CPUARMState *env)
+{
+ return ARMSS_NonSecure;
+}
+
static inline bool arm_is_secure(CPUARMState *env)
{
return false;
ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A,
ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A,
- /* TLBs with 1-1 mapping to the physical address spaces. */
- ARMMMUIdx_Phys_NS = 8 | ARM_MMU_IDX_A,
- ARMMMUIdx_Phys_S = 9 | ARM_MMU_IDX_A,
-
/*
* Used for second stage of an S12 page table walk, or for descriptor
* loads during first stage of an S1 page table walk. Note that both
* are in use simultaneously for SecureEL2: the security state for
* the S2 ptw is selected by the NS bit from the S1 ptw.
*/
- ARMMMUIdx_Stage2 = 10 | ARM_MMU_IDX_A,
- ARMMMUIdx_Stage2_S = 11 | ARM_MMU_IDX_A,
+ ARMMMUIdx_Stage2_S = 8 | ARM_MMU_IDX_A,
+ ARMMMUIdx_Stage2 = 9 | ARM_MMU_IDX_A,
+
+ /* TLBs with 1-1 mapping to the physical address spaces. */
+ ARMMMUIdx_Phys_S = 10 | ARM_MMU_IDX_A,
+ ARMMMUIdx_Phys_NS = 11 | ARM_MMU_IDX_A,
+ ARMMMUIdx_Phys_Root = 12 | ARM_MMU_IDX_A,
+ ARMMMUIdx_Phys_Realm = 13 | ARM_MMU_IDX_A,
/*
* These are not allocated TLBs and are used only for AT system
ARMASIdx_TagS = 3,
} ARMASIdx;
+static inline ARMMMUIdx arm_space_to_phys(ARMSecuritySpace space)
+{
+ /* Assert the relative order of the physical mmu indexes. */
+ QEMU_BUILD_BUG_ON(ARMSS_Secure != 0);
+ QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_NS != ARMMMUIdx_Phys_S + ARMSS_NonSecure);
+ QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_Root != ARMMMUIdx_Phys_S + ARMSS_Root);
+ QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_Realm != ARMMMUIdx_Phys_S + ARMSS_Realm);
+
+ return ARMMMUIdx_Phys_S + space;
+}
+
+static inline ARMSecuritySpace arm_phys_to_space(ARMMMUIdx idx)
+{
+ assert(idx >= ARMMMUIdx_Phys_S && idx <= ARMMMUIdx_Phys_Realm);
+ return idx - ARMMMUIdx_Phys_S;
+}
+
static inline bool arm_v7m_csselr_razwi(ARMCPU *cpu)
{
/* If all the CLIDR.Ctypem bits are 0 there are no caches, and
}
#endif
-void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags);
+void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags);
enum {
QEMU_PSCI_CONDUIT_DISABLED = 0,
return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 0;
}
+static inline bool isar_feature_aa64_rme(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0;
+}
+
static inline bool isar_feature_aa64_vh(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0;
}
if (cpu_isar_feature(aa64_sel2, cpu)) {
valid_mask |= SCR_EEL2;
+ } else if (cpu_isar_feature(aa64_rme, cpu)) {
+ /* With RME and without SEL2, NS is RES1 (R_GSWWH, I_DJJQJ). */
+ value |= SCR_NS;
}
if (cpu_isar_feature(aa64_mte, cpu)) {
valid_mask |= SCR_ATA;
if (cpu_isar_feature(aa64_fgt, cpu)) {
valid_mask |= SCR_FGTEN;
}
+ if (cpu_isar_feature(aa64_rme, cpu)) {
+ valid_mask |= SCR_NSE | SCR_GPF;
+ }
} else {
valid_mask &= ~(SCR_RW | SCR_ST);
if (cpu_isar_feature(aa32_ras, cpu)) {
env->cp15.scr_el3 = value;
/*
- * If SCR_EL3.NS changes, i.e. arm_is_secure_below_el3, then
+ * If SCR_EL3.{NS,NSE} changes, i.e. change of security state,
* we must invalidate all TLBs below EL3.
*/
- if (changed & SCR_NS) {
+ if (changed & (SCR_NS | SCR_NSE)) {
tlb_flush_by_mmuidx(env_cpu(env), (ARMMMUIdxBit_E10_0 |
ARMMMUIdxBit_E20_0 |
ARMMMUIdxBit_E10_1 |
if (cpu_isar_feature(aa64_fwb, cpu)) {
valid_mask |= HCR_FWB;
}
+ if (cpu_isar_feature(aa64_rme, cpu)) {
+ valid_mask |= HCR_GPF;
+ }
}
if (cpu_isar_feature(any_evt, cpu)) {
.access = PL2_RW, .accessfn = access_esm,
.type = ARM_CP_CONST, .resetvalue = 0 },
};
+
+static void tlbi_aa64_paall_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ CPUState *cs = env_cpu(env);
+
+ tlb_flush(cs);
+}
+
+static void gpccr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* L0GPTSZ is RO; other bits not mentioned are RES0. */
+ uint64_t rw_mask = R_GPCCR_PPS_MASK | R_GPCCR_IRGN_MASK |
+ R_GPCCR_ORGN_MASK | R_GPCCR_SH_MASK | R_GPCCR_PGS_MASK |
+ R_GPCCR_GPC_MASK | R_GPCCR_GPCP_MASK;
+
+ env->cp15.gpccr_el3 = (value & rw_mask) | (env->cp15.gpccr_el3 & ~rw_mask);
+}
+
+static void gpccr_reset(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ env->cp15.gpccr_el3 = FIELD_DP64(0, GPCCR, L0GPTSZ,
+ env_archcpu(env)->reset_l0gptsz);
+}
+
+static void tlbi_aa64_paallos_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ CPUState *cs = env_cpu(env);
+
+ tlb_flush_all_cpus_synced(cs);
+}
+
+static const ARMCPRegInfo rme_reginfo[] = {
+ { .name = "GPCCR_EL3", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 6,
+ .access = PL3_RW, .writefn = gpccr_write, .resetfn = gpccr_reset,
+ .fieldoffset = offsetof(CPUARMState, cp15.gpccr_el3) },
+ { .name = "GPTBR_EL3", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 4,
+ .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.gptbr_el3) },
+ { .name = "MFAR_EL3", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 6, .crn = 6, .crm = 0, .opc2 = 5,
+ .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mfar_el3) },
+ { .name = "TLBI_PAALL", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 4,
+ .access = PL3_W, .type = ARM_CP_NO_RAW,
+ .writefn = tlbi_aa64_paall_write },
+ { .name = "TLBI_PAALLOS", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 4,
+ .access = PL3_W, .type = ARM_CP_NO_RAW,
+ .writefn = tlbi_aa64_paallos_write },
+ /*
+ * QEMU does not have a way to invalidate by physical address, thus
+ * invalidating a range of physical addresses is accomplished by
+ * flushing all tlb entries in the outer sharable domain,
+ * just like PAALLOS.
+ */
+ { .name = "TLBI_RPALOS", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 7,
+ .access = PL3_W, .type = ARM_CP_NO_RAW,
+ .writefn = tlbi_aa64_paallos_write },
+ { .name = "TLBI_RPAOS", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 3,
+ .access = PL3_W, .type = ARM_CP_NO_RAW,
+ .writefn = tlbi_aa64_paallos_write },
+ { .name = "DC_CIPAPA", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 1,
+ .access = PL3_W, .type = ARM_CP_NOP },
+};
+
+static const ARMCPRegInfo rme_mte_reginfo[] = {
+ { .name = "DC_CIGDPAPA", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 5,
+ .access = PL3_W, .type = ARM_CP_NOP },
+};
#endif /* TARGET_AARCH64 */
static void define_pmu_regs(ARMCPU *cpu)
if (cpu_isar_feature(aa64_fgt, cpu)) {
define_arm_cp_regs(cpu, fgt_reginfo);
}
+
+ if (cpu_isar_feature(aa64_rme, cpu)) {
+ define_arm_cp_regs(cpu, rme_reginfo);
+ if (cpu_isar_feature(aa64_mte, cpu)) {
+ define_arm_cp_regs(cpu, rme_mte_reginfo);
+ }
+ }
#endif
if (cpu_isar_feature(any_predinv, cpu)) {
[EXCP_UNALIGNED] = "v7M UNALIGNED UsageFault",
[EXCP_DIVBYZERO] = "v7M DIVBYZERO UsageFault",
[EXCP_VSERR] = "Virtual SERR",
+ [EXCP_GPC] = "Granule Protection Check",
};
if (idx >= 0 && idx < ARRAY_SIZE(excnames)) {
}
switch (cs->exception_index) {
+ case EXCP_GPC:
+ qemu_log_mask(CPU_LOG_INT, "...with MFAR 0x%" PRIx64 "\n",
+ env->cp15.mfar_el3);
+ /* fall through */
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
/*
return true;
}
-void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *pflags)
+void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags)
{
CPUARMTBFlags flags;
}
}
#endif
+
+#ifndef CONFIG_USER_ONLY
+ARMSecuritySpace arm_security_space(CPUARMState *env)
+{
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ return arm_secure_to_space(env->v7m.secure);
+ }
+
+ /*
+ * If EL3 is not supported then the secure state is implementation
+ * defined, in which case QEMU defaults to non-secure.
+ */
+ if (!arm_feature(env, ARM_FEATURE_EL3)) {
+ return ARMSS_NonSecure;
+ }
+
+ /* Check for AArch64 EL3 or AArch32 Mon. */
+ if (is_a64(env)) {
+ if (extract32(env->pstate, 2, 2) == 3) {
+ if (cpu_isar_feature(aa64_rme, env_archcpu(env))) {
+ return ARMSS_Root;
+ } else {
+ return ARMSS_Secure;
+ }
+ }
+ } else {
+ if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON) {
+ return ARMSS_Secure;
+ }
+ }
+
+ return arm_security_space_below_el3(env);
+}
+
+ARMSecuritySpace arm_security_space_below_el3(CPUARMState *env)
+{
+ assert(!arm_feature(env, ARM_FEATURE_M));
+
+ /*
+ * If EL3 is not supported then the secure state is implementation
+ * defined, in which case QEMU defaults to non-secure.
+ */
+ if (!arm_feature(env, ARM_FEATURE_EL3)) {
+ return ARMSS_NonSecure;
+ }
+
+ /*
+ * Note NSE cannot be set without RME, and NSE & !NS is Reserved.
+ * Ignoring NSE when !NS retains consistency without having to
+ * modify other predicates.
+ */
+ if (!(env->cp15.scr_el3 & SCR_NS)) {
+ return ARMSS_Secure;
+ } else if (env->cp15.scr_el3 & SCR_NSE) {
+ return ARMSS_Realm;
+ } else {
+ return ARMSS_NonSecure;
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
ARMFault_ICacheMaint,
ARMFault_QEMU_NSCExec, /* v8M: NS executing in S&NSC memory */
ARMFault_QEMU_SFault, /* v8M: SecureFault INVTRAN, INVEP or AUVIOL */
+ ARMFault_GPCFOnWalk,
+ ARMFault_GPCFOnOutput,
} ARMFaultType;
+typedef enum ARMGPCF {
+ GPCF_None,
+ GPCF_AddressSize,
+ GPCF_Walk,
+ GPCF_EABT,
+ GPCF_Fail,
+} ARMGPCF;
+
/**
* ARMMMUFaultInfo: Information describing an ARM MMU Fault
* @type: Type of fault
+ * @gpcf: Subtype of ARMFault_GPCFOn{Walk,Output}.
* @level: Table walk level (for translation, access flag and permission faults)
* @domain: Domain of the fault address (for non-LPAE CPUs only)
* @s2addr: Address that caused a fault at stage 2
+ * @paddr: physical address that caused a fault for gpc
+ * @paddr_space: physical address space that caused a fault for gpc
* @stage2: True if we faulted at stage 2
* @s1ptw: True if we faulted at stage 2 while doing a stage 1 page-table walk
* @s1ns: True if we faulted on a non-secure IPA while in secure state
typedef struct ARMMMUFaultInfo ARMMMUFaultInfo;
struct ARMMMUFaultInfo {
ARMFaultType type;
+ ARMGPCF gpcf;
target_ulong s2addr;
+ target_ulong paddr;
+ ARMSecuritySpace paddr_space;
int level;
int domain;
bool stage2;
case ARMFault_Exclusive:
fsc = 0x35;
break;
+ case ARMFault_GPCFOnWalk:
+ assert(fi->level >= -1 && fi->level <= 3);
+ if (fi->level < 0) {
+ fsc = 0b100011;
+ } else {
+ fsc = 0b100100 | fi->level;
+ }
+ break;
+ case ARMFault_GPCFOnOutput:
+ fsc = 0b101000;
+ break;
default:
/* Other faults can't occur in a context that requires a
* long-format status code.
typedef struct S1Translate {
ARMMMUIdx in_mmu_idx;
ARMMMUIdx in_ptw_idx;
+ ARMSecuritySpace in_space;
bool in_secure;
bool in_debug;
+ /*
+ * If this is stage 2 of a stage 1+2 page table walk, then this must
+ * be true if stage 1 is an EL0 access; otherwise this is ignored.
+ * Stage 2 is indicated by in_mmu_idx set to ARMMMUIdx_Stage2{,_S}.
+ */
+ bool in_s1_is_el0;
bool out_secure;
bool out_rw;
bool out_be;
+ ARMSecuritySpace out_space;
hwaddr out_virt;
hwaddr out_phys;
void *out_host;
} S1Translate;
-static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
- uint64_t address,
- MMUAccessType access_type, bool s1_is_el0,
- GetPhysAddrResult *result, ARMMMUFaultInfo *fi)
- __attribute__((nonnull));
+static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw,
+ target_ulong address,
+ MMUAccessType access_type,
+ GetPhysAddrResult *result,
+ ARMMMUFaultInfo *fi);
-static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw,
- target_ulong address,
- MMUAccessType access_type,
- GetPhysAddrResult *result,
- ARMMMUFaultInfo *fi)
- __attribute__((nonnull));
+static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw,
+ target_ulong address,
+ MMUAccessType access_type,
+ GetPhysAddrResult *result,
+ ARMMMUFaultInfo *fi);
/* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */
static const uint8_t pamax_map[] = {
case ARMMMUIdx_E3:
break;
- case ARMMMUIdx_Phys_NS:
case ARMMMUIdx_Phys_S:
+ case ARMMMUIdx_Phys_NS:
+ case ARMMMUIdx_Phys_Root:
+ case ARMMMUIdx_Phys_Realm:
/* No translation for physical address spaces. */
return true;
return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0;
}
+static bool granule_protection_check(CPUARMState *env, uint64_t paddress,
+ ARMSecuritySpace pspace,
+ ARMMMUFaultInfo *fi)
+{
+ MemTxAttrs attrs = {
+ .secure = true,
+ .space = ARMSS_Root,
+ };
+ ARMCPU *cpu = env_archcpu(env);
+ uint64_t gpccr = env->cp15.gpccr_el3;
+ unsigned pps, pgs, l0gptsz, level = 0;
+ uint64_t tableaddr, pps_mask, align, entry, index;
+ AddressSpace *as;
+ MemTxResult result;
+ int gpi;
+
+ if (!FIELD_EX64(gpccr, GPCCR, GPC)) {
+ return true;
+ }
+
+ /*
+ * GPC Priority 1 (R_GMGRR):
+ * R_JWCSM: If the configuration of GPCCR_EL3 is invalid,
+ * the access fails as GPT walk fault at level 0.
+ */
+
+ /*
+ * Configuration of PPS to a value exceeding the implemented
+ * physical address size is invalid.
+ */
+ pps = FIELD_EX64(gpccr, GPCCR, PPS);
+ if (pps > FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE)) {
+ goto fault_walk;
+ }
+ pps = pamax_map[pps];
+ pps_mask = MAKE_64BIT_MASK(0, pps);
+
+ switch (FIELD_EX64(gpccr, GPCCR, SH)) {
+ case 0b10: /* outer shareable */
+ break;
+ case 0b00: /* non-shareable */
+ case 0b11: /* inner shareable */
+ /* Inner and Outer non-cacheable requires Outer shareable. */
+ if (FIELD_EX64(gpccr, GPCCR, ORGN) == 0 &&
+ FIELD_EX64(gpccr, GPCCR, IRGN) == 0) {
+ goto fault_walk;
+ }
+ break;
+ default: /* reserved */
+ goto fault_walk;
+ }
+
+ switch (FIELD_EX64(gpccr, GPCCR, PGS)) {
+ case 0b00: /* 4KB */
+ pgs = 12;
+ break;
+ case 0b01: /* 64KB */
+ pgs = 16;
+ break;
+ case 0b10: /* 16KB */
+ pgs = 14;
+ break;
+ default: /* reserved */
+ goto fault_walk;
+ }
+
+ /* Note this field is read-only and fixed at reset. */
+ l0gptsz = 30 + FIELD_EX64(gpccr, GPCCR, L0GPTSZ);
+
+ /*
+ * GPC Priority 2: Secure, Realm or Root address exceeds PPS.
+ * R_CPDSB: A NonSecure physical address input exceeding PPS
+ * does not experience any fault.
+ */
+ if (paddress & ~pps_mask) {
+ if (pspace == ARMSS_NonSecure) {
+ return true;
+ }
+ goto fault_size;
+ }
+
+ /* GPC Priority 3: the base address of GPTBR_EL3 exceeds PPS. */
+ tableaddr = env->cp15.gptbr_el3 << 12;
+ if (tableaddr & ~pps_mask) {
+ goto fault_size;
+ }
+
+ /*
+ * BADDR is aligned per a function of PPS and L0GPTSZ.
+ * These bits of GPTBR_EL3 are RES0, but are not a configuration error,
+ * unlike the RES0 bits of the GPT entries (R_XNKFZ).
+ */
+ align = MAX(pps - l0gptsz + 3, 12);
+ align = MAKE_64BIT_MASK(0, align);
+ tableaddr &= ~align;
+
+ as = arm_addressspace(env_cpu(env), attrs);
+
+ /* Level 0 lookup. */
+ index = extract64(paddress, l0gptsz, pps - l0gptsz);
+ tableaddr += index * 8;
+ entry = address_space_ldq_le(as, tableaddr, attrs, &result);
+ if (result != MEMTX_OK) {
+ goto fault_eabt;
+ }
+
+ switch (extract32(entry, 0, 4)) {
+ case 1: /* block descriptor */
+ if (entry >> 8) {
+ goto fault_walk; /* RES0 bits not 0 */
+ }
+ gpi = extract32(entry, 4, 4);
+ goto found;
+ case 3: /* table descriptor */
+ tableaddr = entry & ~0xf;
+ align = MAX(l0gptsz - pgs - 1, 12);
+ align = MAKE_64BIT_MASK(0, align);
+ if (tableaddr & (~pps_mask | align)) {
+ goto fault_walk; /* RES0 bits not 0 */
+ }
+ break;
+ default: /* invalid */
+ goto fault_walk;
+ }
+
+ /* Level 1 lookup */
+ level = 1;
+ index = extract64(paddress, pgs + 4, l0gptsz - pgs - 4);
+ tableaddr += index * 8;
+ entry = address_space_ldq_le(as, tableaddr, attrs, &result);
+ if (result != MEMTX_OK) {
+ goto fault_eabt;
+ }
+
+ switch (extract32(entry, 0, 4)) {
+ case 1: /* contiguous descriptor */
+ if (entry >> 10) {
+ goto fault_walk; /* RES0 bits not 0 */
+ }
+ /*
+ * Because the softmmu tlb only works on units of TARGET_PAGE_SIZE,
+ * and because we cannot invalidate by pa, and thus will always
+ * flush entire tlbs, we don't actually care about the range here
+ * and can simply extract the GPI as the result.
+ */
+ if (extract32(entry, 8, 2) == 0) {
+ goto fault_walk; /* reserved contig */
+ }
+ gpi = extract32(entry, 4, 4);
+ break;
+ default:
+ index = extract64(paddress, pgs, 4);
+ gpi = extract64(entry, index * 4, 4);
+ break;
+ }
+
+ found:
+ switch (gpi) {
+ case 0b0000: /* no access */
+ break;
+ case 0b1111: /* all access */
+ return true;
+ case 0b1000:
+ case 0b1001:
+ case 0b1010:
+ case 0b1011:
+ if (pspace == (gpi & 3)) {
+ return true;
+ }
+ break;
+ default:
+ goto fault_walk; /* reserved */
+ }
+
+ fi->gpcf = GPCF_Fail;
+ goto fault_common;
+ fault_eabt:
+ fi->gpcf = GPCF_EABT;
+ goto fault_common;
+ fault_size:
+ fi->gpcf = GPCF_AddressSize;
+ goto fault_common;
+ fault_walk:
+ fi->gpcf = GPCF_Walk;
+ fault_common:
+ fi->level = level;
+ fi->paddr = paddress;
+ fi->paddr_space = pspace;
+ return false;
+}
+
static bool S2_attrs_are_device(uint64_t hcr, uint8_t attrs)
{
/*
static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw,
hwaddr addr, ARMMMUFaultInfo *fi)
{
+ ARMSecuritySpace space = ptw->in_space;
bool is_secure = ptw->in_secure;
ARMMMUIdx mmu_idx = ptw->in_mmu_idx;
ARMMMUIdx s2_mmu_idx = ptw->in_ptw_idx;
* From gdbstub, do not use softmmu so that we don't modify the
* state of the cpu at all, including softmmu tlb contents.
*/
- if (regime_is_stage2(s2_mmu_idx)) {
- S1Translate s2ptw = {
- .in_mmu_idx = s2_mmu_idx,
- .in_ptw_idx = ptw_idx_for_stage_2(env, s2_mmu_idx),
- .in_secure = s2_mmu_idx == ARMMMUIdx_Stage2_S,
- .in_debug = true,
- };
- GetPhysAddrResult s2 = { };
-
- if (get_phys_addr_lpae(env, &s2ptw, addr, MMU_DATA_LOAD,
- false, &s2, fi)) {
- goto fail;
- }
- ptw->out_phys = s2.f.phys_addr;
- pte_attrs = s2.cacheattrs.attrs;
- ptw->out_secure = s2.f.attrs.secure;
- } else {
- /* Regime is physical. */
- ptw->out_phys = addr;
- pte_attrs = 0;
- ptw->out_secure = s2_mmu_idx == ARMMMUIdx_Phys_S;
+ S1Translate s2ptw = {
+ .in_mmu_idx = s2_mmu_idx,
+ .in_ptw_idx = ptw_idx_for_stage_2(env, s2_mmu_idx),
+ .in_secure = s2_mmu_idx == ARMMMUIdx_Stage2_S,
+ .in_space = (s2_mmu_idx == ARMMMUIdx_Stage2_S ? ARMSS_Secure
+ : space == ARMSS_Realm ? ARMSS_Realm
+ : ARMSS_NonSecure),
+ .in_debug = true,
+ };
+ GetPhysAddrResult s2 = { };
+
+ if (get_phys_addr_gpc(env, &s2ptw, addr, MMU_DATA_LOAD, &s2, fi)) {
+ goto fail;
}
+
+ ptw->out_phys = s2.f.phys_addr;
+ pte_attrs = s2.cacheattrs.attrs;
ptw->out_host = NULL;
ptw->out_rw = false;
+ ptw->out_secure = s2.f.attrs.secure;
+ ptw->out_space = s2.f.attrs.space;
} else {
#ifdef CONFIG_TCG
CPUTLBEntryFull *full;
ptw->out_rw = full->prot & PAGE_WRITE;
pte_attrs = full->pte_attrs;
ptw->out_secure = full->attrs.secure;
+ ptw->out_space = full->attrs.space;
#else
g_assert_not_reached();
#endif
fail:
assert(fi->type != ARMFault_None);
+ if (fi->type == ARMFault_GPCFOnOutput) {
+ fi->type = ARMFault_GPCFOnWalk;
+ }
fi->s2addr = addr;
fi->stage2 = true;
fi->s1ptw = true;
}
} else {
/* Page tables are in MMIO. */
- MemTxAttrs attrs = { .secure = ptw->out_secure };
+ MemTxAttrs attrs = {
+ .secure = ptw->out_secure,
+ .space = ptw->out_space,
+ };
AddressSpace *as = arm_addressspace(cs, attrs);
MemTxResult result = MEMTX_OK;
#endif
} else {
/* Page tables are in MMIO. */
- MemTxAttrs attrs = { .secure = ptw->out_secure };
+ MemTxAttrs attrs = {
+ .secure = ptw->out_secure,
+ .space = ptw->out_space,
+ };
AddressSpace *as = arm_addressspace(cs, attrs);
MemTxResult result = MEMTX_OK;
* regime, because the attribute will already be non-secure.
*/
result->f.attrs.secure = false;
+ result->f.attrs.space = ARMSS_NonSecure;
}
result->f.phys_addr = phys_addr;
return false;
* @xn: XN (execute-never) bits
* @s1_is_el0: true if this is S2 of an S1+2 walk for EL0
*/
-static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
+static int get_S2prot_noexecute(int s2ap)
{
int prot = 0;
if (s2ap & 2) {
prot |= PAGE_WRITE;
}
+ return prot;
+}
+
+static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
+{
+ int prot = get_S2prot_noexecute(s2ap);
if (cpu_isar_feature(any_tts2uxn, env_archcpu(env))) {
switch (xn) {
* @mmu_idx: MMU index indicating required translation regime
* @is_aa64: TRUE if AArch64
* @ap: The 2-bit simple AP (AP[2:1])
- * @ns: NS (non-secure) bit
* @xn: XN (execute-never) bit
* @pxn: PXN (privileged execute-never) bit
+ * @in_pa: The original input pa space
+ * @out_pa: The output pa space, modified by NSTable, NS, and NSE
*/
static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64,
- int ap, int ns, int xn, int pxn)
+ int ap, int xn, int pxn,
+ ARMSecuritySpace in_pa, ARMSecuritySpace out_pa)
{
ARMCPU *cpu = env_archcpu(env);
bool is_user = regime_is_user(env, mmu_idx);
}
}
- if (ns && arm_is_secure(env) && (env->cp15.scr_el3 & SCR_SIF)) {
- return prot_rw;
+ if (in_pa != out_pa) {
+ switch (in_pa) {
+ case ARMSS_Root:
+ /*
+ * R_ZWRVD: permission fault for insn fetched from non-Root,
+ * I_WWBFB: SIF has no effect in EL3.
+ */
+ return prot_rw;
+ case ARMSS_Realm:
+ /*
+ * R_PKTDS: permission fault for insn fetched from non-Realm,
+ * for Realm EL2 or EL2&0. The corresponding fault for EL1&0
+ * happens during any stage2 translation.
+ */
+ switch (mmu_idx) {
+ case ARMMMUIdx_E2:
+ case ARMMMUIdx_E20_0:
+ case ARMMMUIdx_E20_2:
+ case ARMMMUIdx_E20_2_PAN:
+ return prot_rw;
+ default:
+ break;
+ }
+ break;
+ case ARMSS_Secure:
+ if (env->cp15.scr_el3 & SCR_SIF) {
+ return prot_rw;
+ }
+ break;
+ default:
+ /* Input NonSecure must have output NonSecure. */
+ g_assert_not_reached();
+ }
}
/* TODO have_wxn should be replaced with
* @ptw: Current and next stage parameters for the walk.
* @address: virtual address to get physical address for
* @access_type: MMU_DATA_LOAD, MMU_DATA_STORE or MMU_INST_FETCH
- * @s1_is_el0: if @ptw->in_mmu_idx is ARMMMUIdx_Stage2
- * (so this is a stage 2 page table walk),
- * must be true if this is stage 2 of a stage 1+2
- * walk for an EL0 access. If @mmu_idx is anything else,
- * @s1_is_el0 is ignored.
* @result: set on translation success,
* @fi: set to fault info if the translation fails
*/
static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
uint64_t address,
- MMUAccessType access_type, bool s1_is_el0,
+ MMUAccessType access_type,
GetPhysAddrResult *result, ARMMMUFaultInfo *fi)
{
ARMCPU *cpu = env_archcpu(env);
ARMMMUIdx mmu_idx = ptw->in_mmu_idx;
- bool is_secure = ptw->in_secure;
int32_t level;
ARMVAParameters param;
uint64_t ttbr;
int32_t stride;
int addrsize, inputsize, outputsize;
uint64_t tcr = regime_tcr(env, mmu_idx);
- int ap, ns, xn, pxn;
+ int ap, xn, pxn;
uint32_t el = regime_el(env, mmu_idx);
uint64_t descaddrmask;
bool aarch64 = arm_el_is_aa64(env, el);
uint64_t descriptor, new_descriptor;
- bool nstable;
+ ARMSecuritySpace out_space;
/* TODO: This code does not support shareability levels. */
if (aarch64) {
descaddrmask = MAKE_64BIT_MASK(0, 40);
}
descaddrmask &= ~indexmask_grainsize;
-
- /*
- * Secure stage 1 accesses start with the page table in secure memory and
- * can be downgraded to non-secure at any step. Non-secure accesses
- * remain non-secure. We implement this by just ORing in the NSTable/NS
- * bits at each step.
- * Stage 2 never gets this kind of downgrade.
- */
- tableattrs = is_secure ? 0 : (1 << 4);
+ tableattrs = 0;
next_level:
descaddr |= (address >> (stride * (4 - level))) & indexmask;
descaddr &= ~7ULL;
- nstable = !regime_is_stage2(mmu_idx) && extract32(tableattrs, 4, 1);
- if (nstable) {
+
+ /*
+ * Process the NSTable bit from the previous level. This changes
+ * the table address space and the output space from Secure to
+ * NonSecure. With RME, the EL3 translation regime does not change
+ * from Root to NonSecure.
+ */
+ if (ptw->in_space == ARMSS_Secure
+ && !regime_is_stage2(mmu_idx)
+ && extract32(tableattrs, 4, 1)) {
/*
* Stage2_S -> Stage2 or Phys_S -> Phys_NS
- * Assert that the non-secure idx are even, and relative order.
+ * Assert the relative order of the secure/non-secure indexes.
*/
- QEMU_BUILD_BUG_ON((ARMMMUIdx_Phys_NS & 1) != 0);
- QEMU_BUILD_BUG_ON((ARMMMUIdx_Stage2 & 1) != 0);
- QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_NS + 1 != ARMMMUIdx_Phys_S);
- QEMU_BUILD_BUG_ON(ARMMMUIdx_Stage2 + 1 != ARMMMUIdx_Stage2_S);
- ptw->in_ptw_idx &= ~1;
+ QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_S + 1 != ARMMMUIdx_Phys_NS);
+ QEMU_BUILD_BUG_ON(ARMMMUIdx_Stage2_S + 1 != ARMMMUIdx_Stage2);
+ ptw->in_ptw_idx += 1;
ptw->in_secure = false;
+ ptw->in_space = ARMSS_NonSecure;
}
+
if (!S1_ptw_translate(env, ptw, descaddr, fi)) {
goto do_fault;
}
*/
attrs = new_descriptor & (MAKE_64BIT_MASK(2, 10) | MAKE_64BIT_MASK(50, 14));
if (!regime_is_stage2(mmu_idx)) {
- attrs |= nstable << 5; /* NS */
+ attrs |= !ptw->in_secure << 5; /* NS */
if (!param.hpd) {
attrs |= extract64(tableattrs, 0, 2) << 53; /* XN, PXN */
/*
}
ap = extract32(attrs, 6, 2);
+ out_space = ptw->in_space;
if (regime_is_stage2(mmu_idx)) {
- ns = mmu_idx == ARMMMUIdx_Stage2;
- xn = extract64(attrs, 53, 2);
- result->f.prot = get_S2prot(env, ap, xn, s1_is_el0);
+ /*
+ * R_GYNXY: For stage2 in Realm security state, bit 55 is NS.
+ * The bit remains ignored for other security states.
+ * R_YMCSL: Executing an insn fetched from non-Realm causes
+ * a stage2 permission fault.
+ */
+ if (out_space == ARMSS_Realm && extract64(attrs, 55, 1)) {
+ out_space = ARMSS_NonSecure;
+ result->f.prot = get_S2prot_noexecute(ap);
+ } else {
+ xn = extract64(attrs, 53, 2);
+ result->f.prot = get_S2prot(env, ap, xn, ptw->in_s1_is_el0);
+ }
} else {
- ns = extract32(attrs, 5, 1);
+ int nse, ns = extract32(attrs, 5, 1);
+ switch (out_space) {
+ case ARMSS_Root:
+ /*
+ * R_GVZML: Bit 11 becomes the NSE field in the EL3 regime.
+ * R_XTYPW: NSE and NS together select the output pa space.
+ */
+ nse = extract32(attrs, 11, 1);
+ out_space = (nse << 1) | ns;
+ if (out_space == ARMSS_Secure &&
+ !cpu_isar_feature(aa64_sel2, cpu)) {
+ out_space = ARMSS_NonSecure;
+ }
+ break;
+ case ARMSS_Secure:
+ if (ns) {
+ out_space = ARMSS_NonSecure;
+ }
+ break;
+ case ARMSS_Realm:
+ switch (mmu_idx) {
+ case ARMMMUIdx_Stage1_E0:
+ case ARMMMUIdx_Stage1_E1:
+ case ARMMMUIdx_Stage1_E1_PAN:
+ /* I_CZPRF: For Realm EL1&0 stage1, NS bit is RES0. */
+ break;
+ case ARMMMUIdx_E2:
+ case ARMMMUIdx_E20_0:
+ case ARMMMUIdx_E20_2:
+ case ARMMMUIdx_E20_2_PAN:
+ /*
+ * R_LYKFZ, R_WGRZN: For Realm EL2 and EL2&1,
+ * NS changes the output to non-secure space.
+ */
+ if (ns) {
+ out_space = ARMSS_NonSecure;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+ case ARMSS_NonSecure:
+ /* R_QRMFF: For NonSecure state, the NS bit is RES0. */
+ break;
+ default:
+ g_assert_not_reached();
+ }
xn = extract64(attrs, 54, 1);
pxn = extract64(attrs, 53, 1);
- result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn);
+
+ /*
+ * Note that we modified ptw->in_space earlier for NSTable, but
+ * result->f.attrs retains a copy of the original security space.
+ */
+ result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, xn, pxn,
+ result->f.attrs.space, out_space);
}
if (!(result->f.prot & (1 << access_type))) {
}
}
- if (ns) {
- /*
- * The NS bit will (as required by the architecture) have no effect if
- * the CPU doesn't support TZ or this is a non-secure translation
- * regime, because the attribute will already be non-secure.
- */
- result->f.attrs.secure = false;
- }
+ result->f.attrs.space = out_space;
+ result->f.attrs.secure = arm_space_is_secure(out_space);
if (regime_is_stage2(mmu_idx)) {
result->cacheattrs.is_s2_format = true;
*/
if (sattrs.ns) {
result->f.attrs.secure = false;
+ result->f.attrs.space = ARMSS_NonSecure;
} else if (!secure) {
/*
* NS access to S memory must fault.
ARMMMUFaultInfo *fi)
{
uint8_t memattr = 0x00; /* Device nGnRnE */
- uint8_t shareability = 0; /* non-sharable */
+ uint8_t shareability = 0; /* non-shareable */
int r_el;
switch (mmu_idx) {
case ARMMMUIdx_Stage2:
case ARMMMUIdx_Stage2_S:
- case ARMMMUIdx_Phys_NS:
case ARMMMUIdx_Phys_S:
+ case ARMMMUIdx_Phys_NS:
+ case ARMMMUIdx_Phys_Root:
+ case ARMMMUIdx_Phys_Realm:
break;
default:
} else {
memattr = 0x44; /* Normal, NC, No */
}
- shareability = 2; /* outer sharable */
+ shareability = 2; /* outer shareable */
}
result->cacheattrs.is_s2_format = false;
break;
bool is_secure = ptw->in_secure;
bool ret, ipa_secure;
ARMCacheAttrs cacheattrs1;
- bool is_el0;
+ ARMSecuritySpace ipa_space;
uint64_t hcr;
- ret = get_phys_addr_with_struct(env, ptw, address, access_type, result, fi);
+ ret = get_phys_addr_nogpc(env, ptw, address, access_type, result, fi);
/* If S1 fails, return early. */
if (ret) {
ipa = result->f.phys_addr;
ipa_secure = result->f.attrs.secure;
+ ipa_space = result->f.attrs.space;
- is_el0 = ptw->in_mmu_idx == ARMMMUIdx_Stage1_E0;
+ ptw->in_s1_is_el0 = ptw->in_mmu_idx == ARMMMUIdx_Stage1_E0;
ptw->in_mmu_idx = ipa_secure ? ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2;
ptw->in_secure = ipa_secure;
+ ptw->in_space = ipa_space;
ptw->in_ptw_idx = ptw_idx_for_stage_2(env, ptw->in_mmu_idx);
/*
cacheattrs1 = result->cacheattrs;
memset(result, 0, sizeof(*result));
- if (arm_feature(env, ARM_FEATURE_PMSA)) {
- ret = get_phys_addr_pmsav8(env, ipa, access_type,
- ptw->in_mmu_idx, is_secure, result, fi);
- } else {
- ret = get_phys_addr_lpae(env, ptw, ipa, access_type,
- is_el0, result, fi);
- }
+ ret = get_phys_addr_nogpc(env, ptw, ipa, access_type, result, fi);
fi->s2addr = ipa;
/* Combine the S1 and S2 perms. */
return false;
}
-static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw,
+static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw,
target_ulong address,
MMUAccessType access_type,
GetPhysAddrResult *result,
ARMMMUIdx s1_mmu_idx;
/*
- * The page table entries may downgrade secure to non-secure, but
- * cannot upgrade an non-secure translation regime's attributes
- * to secure.
+ * The page table entries may downgrade Secure to NonSecure, but
+ * cannot upgrade a NonSecure translation regime's attributes
+ * to Secure or Realm.
*/
result->f.attrs.secure = is_secure;
+ result->f.attrs.space = ptw->in_space;
switch (mmu_idx) {
case ARMMMUIdx_Phys_S:
case ARMMMUIdx_Phys_NS:
+ case ARMMMUIdx_Phys_Root:
+ case ARMMMUIdx_Phys_Realm:
/* Checking Phys early avoids special casing later vs regime_el. */
return get_phys_addr_disabled(env, address, access_type, mmu_idx,
is_secure, result, fi);
default:
/* Single stage uses physical for ptw. */
- ptw->in_ptw_idx = is_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS;
+ ptw->in_ptw_idx = arm_space_to_phys(ptw->in_space);
break;
}
}
if (regime_using_lpae_format(env, mmu_idx)) {
- return get_phys_addr_lpae(env, ptw, address, access_type, false,
- result, fi);
+ return get_phys_addr_lpae(env, ptw, address, access_type, result, fi);
} else if (arm_feature(env, ARM_FEATURE_V7) ||
regime_sctlr(env, mmu_idx) & SCTLR_XP) {
return get_phys_addr_v6(env, ptw, address, access_type, result, fi);
}
}
+static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw,
+ target_ulong address,
+ MMUAccessType access_type,
+ GetPhysAddrResult *result,
+ ARMMMUFaultInfo *fi)
+{
+ if (get_phys_addr_nogpc(env, ptw, address, access_type, result, fi)) {
+ return true;
+ }
+ if (!granule_protection_check(env, result->f.phys_addr,
+ result->f.attrs.space, fi)) {
+ fi->type = ARMFault_GPCFOnOutput;
+ return true;
+ }
+ return false;
+}
+
bool get_phys_addr_with_secure(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
bool is_secure, GetPhysAddrResult *result,
S1Translate ptw = {
.in_mmu_idx = mmu_idx,
.in_secure = is_secure,
+ .in_space = arm_secure_to_space(is_secure),
};
- return get_phys_addr_with_struct(env, &ptw, address, access_type,
- result, fi);
+ return get_phys_addr_gpc(env, &ptw, address, access_type, result, fi);
}
bool get_phys_addr(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
GetPhysAddrResult *result, ARMMMUFaultInfo *fi)
{
- bool is_secure;
+ S1Translate ptw = {
+ .in_mmu_idx = mmu_idx,
+ };
+ ARMSecuritySpace ss;
switch (mmu_idx) {
case ARMMMUIdx_E10_0:
case ARMMMUIdx_Stage1_E1:
case ARMMMUIdx_Stage1_E1_PAN:
case ARMMMUIdx_E2:
- is_secure = arm_is_secure_below_el3(env);
+ ss = arm_security_space_below_el3(env);
break;
case ARMMMUIdx_Stage2:
+ /*
+ * For Secure EL2, we need this index to be NonSecure;
+ * otherwise this will already be NonSecure or Realm.
+ */
+ ss = arm_security_space_below_el3(env);
+ if (ss == ARMSS_Secure) {
+ ss = ARMSS_NonSecure;
+ }
+ break;
case ARMMMUIdx_Phys_NS:
case ARMMMUIdx_MPrivNegPri:
case ARMMMUIdx_MUserNegPri:
case ARMMMUIdx_MPriv:
case ARMMMUIdx_MUser:
- is_secure = false;
+ ss = ARMSS_NonSecure;
break;
- case ARMMMUIdx_E3:
case ARMMMUIdx_Stage2_S:
case ARMMMUIdx_Phys_S:
case ARMMMUIdx_MSPrivNegPri:
case ARMMMUIdx_MSUserNegPri:
case ARMMMUIdx_MSPriv:
case ARMMMUIdx_MSUser:
- is_secure = true;
+ ss = ARMSS_Secure;
+ break;
+ case ARMMMUIdx_E3:
+ if (arm_feature(env, ARM_FEATURE_AARCH64) &&
+ cpu_isar_feature(aa64_rme, env_archcpu(env))) {
+ ss = ARMSS_Root;
+ } else {
+ ss = ARMSS_Secure;
+ }
+ break;
+ case ARMMMUIdx_Phys_Root:
+ ss = ARMSS_Root;
+ break;
+ case ARMMMUIdx_Phys_Realm:
+ ss = ARMSS_Realm;
break;
default:
g_assert_not_reached();
}
- return get_phys_addr_with_secure(env, address, access_type, mmu_idx,
- is_secure, result, fi);
+
+ ptw.in_space = ss;
+ ptw.in_secure = arm_space_is_secure(ss);
+ return get_phys_addr_gpc(env, &ptw, address, access_type, result, fi);
}
hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
+ ARMMMUIdx mmu_idx = arm_mmu_idx(env);
+ ARMSecuritySpace ss = arm_security_space(env);
S1Translate ptw = {
- .in_mmu_idx = arm_mmu_idx(env),
- .in_secure = arm_is_secure(env),
+ .in_mmu_idx = mmu_idx,
+ .in_space = ss,
+ .in_secure = arm_space_is_secure(ss),
.in_debug = true,
};
GetPhysAddrResult res = {};
ARMMMUFaultInfo fi = {};
bool ret;
- ret = get_phys_addr_with_struct(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi);
+ ret = get_phys_addr_gpc(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi);
*attrs = res.f.attrs;
if (ret) {
EC_SVEACCESSTRAP = 0x19,
EC_ERETTRAP = 0x1a,
EC_SMETRAP = 0x1d,
+ EC_GPC = 0x1e,
EC_INSNABORT = 0x20,
EC_INSNABORT_SAME_EL = 0x21,
EC_PCALIGNMENT = 0x22,
(cv << 24) | (cond << 20) | rm;
}
+static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc,
+ int cm, int s1ptw, int wnr, int fsc)
+{
+ /* TODO: FEAT_NV2 adds VNCR */
+ return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21)
+ | (ind << 20) | (gpcsc << 14) | (cm << 8) | (s1ptw << 7)
+ | (wnr << 6) | fsc;
+}
+
static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc)
{
return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)
cpu->sve_max_vq = max_vq;
}
+static bool cpu_arm_get_rme(Object *obj, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ return cpu_isar_feature(aa64_rme, cpu);
+}
+
+static void cpu_arm_set_rme(Object *obj, bool value, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint64_t t;
+
+ t = cpu->isar.id_aa64pfr0;
+ t = FIELD_DP64(t, ID_AA64PFR0, RME, value);
+ cpu->isar.id_aa64pfr0 = t;
+}
+
+static void cpu_max_set_l0gptsz(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint32_t value;
+
+ if (!visit_type_uint32(v, name, &value, errp)) {
+ return;
+ }
+
+ /* Encode the value for the GPCCR_EL3 field. */
+ switch (value) {
+ case 30:
+ case 34:
+ case 36:
+ case 39:
+ cpu->reset_l0gptsz = value - 30;
+ break;
+ default:
+ error_setg(errp, "invalid value for l0gptsz");
+ error_append_hint(errp, "valid values are 30, 34, 36, 39\n");
+ break;
+ }
+}
+
+static void cpu_max_get_l0gptsz(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint32_t value = cpu->reset_l0gptsz + 30;
+
+ visit_type_uint32(v, name, &value, errp);
+}
+
static Property arm_cpu_lpa2_property =
DEFINE_PROP_BOOL("lpa2", ARMCPU, prop_lpa2, true);
aarch64_add_sme_properties(obj);
object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
cpu_max_set_sve_max_vq, NULL, NULL);
+ object_property_add_bool(obj, "x-rme", cpu_arm_get_rme, cpu_arm_set_rme);
+ object_property_add(obj, "x-l0gptsz", "uint32", cpu_max_get_l0gptsz,
+ cpu_max_set_l0gptsz, NULL, NULL);
qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property);
}
return fsr;
}
+static bool report_as_gpc_exception(ARMCPU *cpu, int current_el,
+ ARMMMUFaultInfo *fi)
+{
+ bool ret;
+
+ switch (fi->gpcf) {
+ case GPCF_None:
+ return false;
+ case GPCF_AddressSize:
+ case GPCF_Walk:
+ case GPCF_EABT:
+ /* R_PYTGX: GPT faults are reported as GPC. */
+ ret = true;
+ break;
+ case GPCF_Fail:
+ /*
+ * R_BLYPM: A GPF at EL3 is reported as insn or data abort.
+ * R_VBZMW, R_LXHQR: A GPF at EL[0-2] is reported as a GPC
+ * if SCR_EL3.GPF is set, otherwise an insn or data abort.
+ */
+ ret = (cpu->env.cp15.scr_el3 & SCR_GPF) && current_el != 3;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ assert(cpu_isar_feature(aa64_rme, cpu));
+ assert(fi->type == ARMFault_GPCFOnWalk ||
+ fi->type == ARMFault_GPCFOnOutput);
+ if (fi->gpcf == GPCF_AddressSize) {
+ assert(fi->level == 0);
+ } else {
+ assert(fi->level >= 0 && fi->level <= 1);
+ }
+
+ return ret;
+}
+
+static unsigned encode_gpcsc(ARMMMUFaultInfo *fi)
+{
+ static uint8_t const gpcsc[] = {
+ [GPCF_AddressSize] = 0b000000,
+ [GPCF_Walk] = 0b000100,
+ [GPCF_Fail] = 0b001100,
+ [GPCF_EABT] = 0b010100,
+ };
+
+ /* Note that we've validated fi->gpcf and fi->level above. */
+ return gpcsc[fi->gpcf] | fi->level;
+}
+
static G_NORETURN
void arm_deliver_fault(ARMCPU *cpu, vaddr addr,
MMUAccessType access_type,
int mmu_idx, ARMMMUFaultInfo *fi)
{
CPUARMState *env = &cpu->env;
- int target_el;
+ int target_el = exception_target_el(env);
+ int current_el = arm_current_el(env);
bool same_el;
uint32_t syn, exc, fsr, fsc;
- target_el = exception_target_el(env);
+ if (report_as_gpc_exception(cpu, current_el, fi)) {
+ target_el = 3;
+
+ fsr = compute_fsr_fsc(env, fi, target_el, mmu_idx, &fsc);
+
+ syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk,
+ access_type == MMU_INST_FETCH,
+ encode_gpcsc(fi), 0, fi->s1ptw,
+ access_type == MMU_DATA_STORE, fsc);
+
+ env->cp15.mfar_el3 = fi->paddr;
+ switch (fi->paddr_space) {
+ case ARMSS_Secure:
+ break;
+ case ARMSS_NonSecure:
+ env->cp15.mfar_el3 |= R_MFAR_NS_MASK;
+ break;
+ case ARMSS_Root:
+ env->cp15.mfar_el3 |= R_MFAR_NSE_MASK;
+ break;
+ case ARMSS_Realm:
+ env->cp15.mfar_el3 |= R_MFAR_NSE_MASK | R_MFAR_NS_MASK;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ exc = EXCP_GPC;
+ goto do_raise;
+ }
+
+ /* If SCR_EL3.GPF is unset, GPF may still be routed to EL2. */
+ if (fi->gpcf == GPCF_Fail && target_el < 2) {
+ if (arm_hcr_el2_eff(env) & HCR_GPF) {
+ target_el = 2;
+ }
+ }
+
if (fi->stage2) {
target_el = 2;
env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4;
env->cp15.hpfar_el2 |= HPFAR_NS;
}
}
- same_el = (arm_current_el(env) == target_el);
+ same_el = current_el == target_el;
fsr = compute_fsr_fsc(env, fi, target_el, mmu_idx, &fsc);
if (access_type == MMU_INST_FETCH) {
exc = EXCP_DATA_ABORT;
}
+ do_raise:
env->exception.vaddress = addr;
env->exception.fsr = fsr;
raise_exception(env, exc, syn, target_el);
/* Predicate register stores can be any multiple of 2. */
if (len_remain >= 8) {
t0 = tcg_temp_new_i64();
- tcg_gen_st_i64(t0, base, vofs + len_align);
+ tcg_gen_ld_i64(t0, base, vofs + len_align);
tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE);
len_remain -= 8;
len_align += 8;
TB_FLAGS_SKIP = 2,
};
-static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *pflags)
+static inline void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags)
{
uint32_t flags = 0;
#include "exec/cpu-all.h"
-static inline void cpu_get_tb_cpu_state(CPUCRISState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUCRISState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1)
-static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
uint32_t hex_flags = 0;
*pc = env->gpr[HEX_REG_PC];
#define TB_FLAG_PRIV_SHIFT 8
#define TB_FLAG_UNALIGN 0x400
-static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc,
- target_ulong *cs_base,
- uint32_t *pflags)
+static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags)
{
uint32_t flags = env->psw_n * PSW_N;
CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER */
#ifdef TARGET_X86_64
-#define TCG_EXT2_X86_64_FEATURES (CPUID_EXT2_SYSCALL | CPUID_EXT2_LM)
+#define TCG_EXT2_X86_64_FEATURES CPUID_EXT2_LM
#else
#define TCG_EXT2_X86_64_FEATURES 0
#endif
#define TCG_EXT2_FEATURES ((TCG_FEATURES & CPUID_EXT2_AMD_ALIASES) | \
CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \
CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_PDPE1GB | \
- TCG_EXT2_X86_64_FEATURES)
+ CPUID_EXT2_SYSCALL | TCG_EXT2_X86_64_FEATURES)
#define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \
- CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A)
+ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A | \
+ CPUID_EXT3_3DNOWPREFETCH)
#define TCG_EXT4_FEATURES 0
#define TCG_SVM_FEATURES (CPUID_SVM_NPT | CPUID_SVM_VGIF | \
CPUID_SVM_SVME_ADDR_CHK)
CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \
CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \
CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \
- CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_AVX2)
+ CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_RDSEED)
/* missing:
CPUID_7_0_EBX_HLE
- CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM,
- CPUID_7_0_EBX_RDSEED */
+ CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM */
+
+#if defined CONFIG_SOFTMMU || defined CONFIG_LINUX
+#define TCG_7_0_ECX_RDPID CPUID_7_0_ECX_RDPID
+#else
+#define TCG_7_0_ECX_RDPID 0
+#endif
#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | \
/* CPUID_7_0_ECX_OSPKE is dynamic */ \
- CPUID_7_0_ECX_LA57 | CPUID_7_0_ECX_PKS | CPUID_7_0_ECX_VAES)
+ CPUID_7_0_ECX_LA57 | CPUID_7_0_ECX_PKS | CPUID_7_0_ECX_VAES | \
+ TCG_7_0_ECX_RDPID)
+
#define TCG_7_0_EDX_FEATURES CPUID_7_0_EDX_FSRM
#define TCG_7_1_EAX_FEATURES (CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | \
CPUID_7_1_EAX_FSRC)
#define TCG_SGX_12_0_EBX_FEATURES 0
#define TCG_SGX_12_1_EAX_FEATURES 0
+#define TCG_8000_0008_EBX (CPUID_8000_0008_EBX_XSAVEERPTR | \
+ CPUID_8000_0008_EBX_WBNOINVD)
+
FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
[FEAT_1_EDX] = {
.type = CPUID_FEATURE_WORD,
"amd-psfd", NULL, NULL, NULL,
},
.cpuid = { .eax = 0x80000008, .reg = R_EBX, },
- .tcg_features = 0,
+ .tcg_features = TCG_8000_0008_EBX,
.unmigratable_flags = 0,
},
[FEAT_8000_0021_EAX] = {
*ecx |= 1 << 1; /* CmpLegacy bit */
}
}
+ if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 &&
+ !(env->hflags & HF_LMA_MASK)) {
+ *edx &= ~CPUID_EXT2_SYSCALL;
+ }
break;
case 0x80000002:
case 0x80000003:
#include "hw/i386/apic.h"
#endif
-static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*cs_base = env->segs[R_CS].base;
*pc = *cs_base + env->eip;
DEF_HELPER_1(sysenter, void, env)
DEF_HELPER_2(sysexit, void, env, int)
-#ifdef TARGET_X86_64
DEF_HELPER_2(syscall, void, env, int)
DEF_HELPER_2(sysret, void, env, int)
-#endif
DEF_HELPER_FLAGS_2(pause, TCG_CALL_NO_WG, noreturn, env, int)
DEF_HELPER_FLAGS_3(raise_interrupt, TCG_CALL_NO_WG, noreturn, env, int, int)
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, int)
DEF_HELPER_FLAGS_1(single_step, TCG_CALL_NO_WG, noreturn, env)
DEF_HELPER_1(rechecking_single_step, void, env)
DEF_HELPER_1(cpuid, void, env)
+DEF_HELPER_FLAGS_1(rdpid, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_1(rdtsc, void, env)
-DEF_HELPER_1(rdtscp, void, env)
DEF_HELPER_FLAGS_1(rdpmc, TCG_CALL_NO_WG, noreturn, env)
#ifndef CONFIG_USER_ONLY
env->regs[R_EDX] = (uint32_t)(val >> 32);
}
-void helper_rdtscp(CPUX86State *env)
-{
- helper_rdtsc(env);
- env->regs[R_ECX] = (uint32_t)(env->tsc_aux);
-}
-
G_NORETURN void helper_rdpmc(CPUX86State *env)
{
if (((env->cr[4] & CR4_PCE_MASK) == 0 ) &&
env->pkru = val;
tlb_flush(cs);
}
+
+target_ulong HELPER(rdpid)(CPUX86State *env)
+{
+#if defined CONFIG_SOFTMMU
+ return env->tsc_aux;
+#elif defined CONFIG_LINUX && defined CONFIG_GETCPU
+ unsigned cpu, node;
+ getcpu(&cpu, &node);
+ return (node << 12) | (cpu & 0xfff);
+#elif defined CONFIG_SCHED_GETCPU
+ return sched_getcpu();
+#else
+ return 0;
+#endif
+}
e2);
env->eip = offset;
}
+#endif /* TARGET_X86_64 */
void helper_sysret(CPUX86State *env, int dflag)
{
raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
}
selector = (env->star >> 48) & 0xffff;
+#ifdef TARGET_X86_64
if (env->hflags & HF_LMA_MASK) {
cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK
| ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK |
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
DESC_W_MASK | DESC_A_MASK);
- } else {
+ } else
+#endif
+ {
env->eflags |= IF_MASK;
cpu_x86_load_seg_cache(env, R_CS, selector | 3,
0, 0xffffffff,
DESC_W_MASK | DESC_A_MASK);
}
}
-#endif /* TARGET_X86_64 */
/* real mode interrupt */
static void do_interrupt_real(CPUX86State *env, int intno, int is_int,
#include "tcg/helper-tcg.h"
#include "../seg_helper.h"
-#ifdef TARGET_X86_64
void helper_syscall(CPUX86State *env, int next_eip_addend)
{
int selector;
raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
}
selector = (env->star >> 32) & 0xffff;
+#ifdef TARGET_X86_64
if (env->hflags & HF_LMA_MASK) {
int code64;
} else {
env->eip = env->cstar;
}
- } else {
+ } else
+#endif
+ {
env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend);
env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
env->eip = (uint32_t)env->star;
}
}
-#endif /* TARGET_X86_64 */
void handle_even_inj(CPUX86State *env, int intno, int is_int,
int error_code, int is_hw, int rm)
gen_cmpxchg8b(s, env, modrm);
break;
- case 7: /* RDSEED */
+ case 7: /* RDSEED, RDPID with f3 prefix */
+ if (mod != 3 ||
+ (s->prefix & (PREFIX_LOCK | PREFIX_REPNZ))) {
+ goto illegal_op;
+ }
+ if (s->prefix & PREFIX_REPZ) {
+ if (!(s->cpuid_ext_features & CPUID_7_0_ECX_RDPID)) {
+ goto illegal_op;
+ }
+ gen_helper_rdpid(s->T0, cpu_env);
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_reg_v(s, dflag, rm, s->T0);
+ break;
+ } else {
+ if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_RDSEED)) {
+ goto illegal_op;
+ }
+ goto do_rdrand;
+ }
+
case 6: /* RDRAND */
if (mod != 3 ||
(s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) ||
!(s->cpuid_ext_features & CPUID_EXT_RDRAND)) {
goto illegal_op;
}
+ do_rdrand:
translator_io_start(&s->base);
gen_helper_rdrand(s->T0, cpu_env);
rm = (modrm & 7) | REX_B(s);
s->base.is_jmp = DISAS_NORETURN;
break;
case 0x134: /* sysenter */
- /* For Intel SYSENTER is valid on 64-bit */
- if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1)
+ /* For AMD SYSENTER is not valid in long mode */
+ if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) {
goto illegal_op;
+ }
if (!PE(s)) {
gen_exception_gpf(s);
} else {
}
break;
case 0x135: /* sysexit */
- /* For Intel SYSEXIT is valid on 64-bit */
- if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1)
+ /* For AMD SYSEXIT is not valid in long mode */
+ if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) {
goto illegal_op;
- if (!PE(s)) {
+ }
+ if (!PE(s) || CPL(s) != 0) {
gen_exception_gpf(s);
} else {
gen_helper_sysexit(cpu_env, tcg_constant_i32(dflag - 1));
s->base.is_jmp = DISAS_EOB_ONLY;
}
break;
-#ifdef TARGET_X86_64
case 0x105: /* syscall */
- /* XXX: is it usable in real mode ? */
+ /* For Intel SYSCALL is only valid in long mode */
+ if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) {
+ goto illegal_op;
+ }
gen_update_cc_op(s);
gen_update_eip_cur(s);
gen_helper_syscall(cpu_env, cur_insn_len_i32(s));
gen_eob_worker(s, false, true);
break;
case 0x107: /* sysret */
- if (!PE(s)) {
+ /* For Intel SYSRET is only valid in long mode */
+ if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) {
+ goto illegal_op;
+ }
+ if (!PE(s) || CPL(s) != 0) {
gen_exception_gpf(s);
} else {
gen_helper_sysret(cpu_env, tcg_constant_i32(dflag - 1));
gen_eob_worker(s, false, true);
}
break;
-#endif
case 0x1a2: /* cpuid */
gen_update_cc_op(s);
gen_update_eip_cur(s);
gen_update_cc_op(s);
gen_update_eip_cur(s);
translator_io_start(&s->base);
- gen_helper_rdtscp(cpu_env);
+ gen_helper_rdtsc(cpu_env);
+ gen_helper_rdpid(s->T0, cpu_env);
+ gen_op_mov_reg_v(s, dflag, R_ECX, s->T0);
break;
default:
break;
case 0x108: /* invd */
- case 0x109: /* wbinvd */
+ case 0x109: /* wbinvd; wbnoinvd with REPZ prefix */
if (check_cpl0(s)) {
- gen_svm_check_intercept(s, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD);
+ gen_svm_check_intercept(s, (b & 1) ? SVM_EXIT_WBINVD : SVM_EXIT_INVD);
/* nothing to do */
}
break;
#include "tcg/helper-tcg.h"
#include "tcg/seg_helper.h"
-#ifdef TARGET_X86_64
void helper_syscall(CPUX86State *env, int next_eip_addend)
{
CPUState *cs = env_cpu(env);
env->exception_next_eip = env->eip + next_eip_addend;
cpu_loop_exit(cs);
}
-#endif /* TARGET_X86_64 */
/*
* fake user mode interrupt. is_int is TRUE if coming from the int
#define HW_FLAGS_EUEN_FPE 0x04
#define HW_FLAGS_EUEN_SXE 0x08
-static inline void cpu_get_tb_cpu_state(CPULoongArchState *env,
- target_ulong *pc,
- target_ulong *cs_base,
- uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
#define TB_FLAGS_TRACE 16
#define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE)
-static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUM68KState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
#include "exec/cpu-defs.h"
#include "qemu/cpu-float.h"
+/* MicroBlaze is always in-order. */
+#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL
+
typedef struct CPUArchState CPUMBState;
#if !defined(CONFIG_USER_ONLY)
#include "mmu.h"
/* Ensure there is no overlap between the two masks. */
QEMU_BUILD_BUG_ON(MSR_TB_MASK & IFLAGS_TB_MASK);
-static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUMBState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK);
/* helper.c */
target_ulong exception_resume_pc(CPUMIPSState *env);
-static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->active_tc.PC;
*cs_base = 0;
FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */
-static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUNios2State *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
env->shadow_gpr[0][i] = val;
}
-static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env,
- target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
POWERPC_FLAG_TM = 0x00100000,
/* Has SCV (ISA 3.00) */
POWERPC_FLAG_SCV = 0x00200000,
+ /* Has >1 thread per core */
+ POWERPC_FLAG_SMT = 0x00400000,
};
/*
uint64_t pmu_base_time;
};
+#define _CORE_ID(cs) \
+ (POWERPC_CPU(cs)->env.spr_cb[SPR_PIR].default_value & ~(cs->nr_threads - 1))
+
+#define THREAD_SIBLING_FOREACH(cs, cs_sibling) \
+ CPU_FOREACH(cs_sibling) \
+ if (_CORE_ID(cs) == _CORE_ID(cs_sibling))
+
#define SET_FIT_PERIOD(a_, b_, c_, d_) \
do { \
env->fit_period[0] = (a_); \
#define SPR_HMER (0x150)
#define SPR_HMEER (0x151)
#define SPR_PCR (0x152)
+#define SPR_HEIR (0x153)
#define SPR_BOOKE_LPIDR (0x152)
#define SPR_BOOKE_TCR (0x154)
#define SPR_BOOKE_TLB0PS (0x158)
#define is_book3s_arch2x(ctx) (!!((ctx)->insns_flags & PPC_SEGMENT_64B))
#ifdef CONFIG_DEBUG_TCG
-void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags);
+void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags);
#else
-static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->nip;
*cs_base = 0;
* HSRR0 => SPR 314 (Power 2.04 hypv)
* HSRR1 => SPR 315 (Power 2.04 hypv)
* LPIDR => SPR 317 (970)
+ * HEIR => SPR 339 (Power 2.05 hypv) (64-bit reg from 3.1)
* EPR => SPR 702 (Power 2.04 emb)
* perf => 768-783 (Power 2.04)
* perf => 784-799 (Power 2.04)
0x00000000);
}
+static void register_HEIR32_spr(CPUPPCState *env)
+{
+ spr_register_hv(env, SPR_HEIR, "HEIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic32,
+ 0x00000000);
+}
+
+static void register_HEIR64_spr(CPUPPCState *env)
+{
+ spr_register_hv(env, SPR_HEIR, "HEIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
static void register_power8_tce_address_control_sprs(CPUPPCState *env)
{
spr_register_kvm(env, SPR_TAR, "TAR",
register_power5p_ear_sprs(env);
register_power5p_tb_sprs(env);
register_power6_common_sprs(env);
+ register_HEIR32_spr(env);
register_power6_dbg_sprs(env);
register_power7_book4_sprs(env);
register_power5p_ear_sprs(env);
register_power5p_tb_sprs(env);
register_power6_common_sprs(env);
+ register_HEIR32_spr(env);
register_power6_dbg_sprs(env);
register_power8_tce_address_control_sprs(env);
register_power8_ids_sprs(env);
register_power5p_ear_sprs(env);
register_power5p_tb_sprs(env);
register_power6_common_sprs(env);
+ register_HEIR32_spr(env);
register_power6_dbg_sprs(env);
register_power8_tce_address_control_sprs(env);
register_power8_ids_sprs(env);
register_power5p_ear_sprs(env);
register_power5p_tb_sprs(env);
register_power6_common_sprs(env);
+ register_HEIR64_spr(env);
register_power6_dbg_sprs(env);
register_power8_tce_address_control_sprs(env);
register_power8_ids_sprs(env);
{
CPUState *cs = CPU(dev);
PowerPCCPU *cpu = POWERPC_CPU(dev);
+ CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
Error *local_err = NULL;
pcc->parent_realize(dev, errp);
+ if (env_cpu(env)->nr_threads > 1) {
+ env->flags |= POWERPC_FLAG_SMT;
+ }
+
return;
unrealize:
#include "trace.h"
#ifdef CONFIG_TCG
+#include "sysemu/tcg.h"
#include "exec/helper-proto.h"
#include "exec/cpu_ldst.h"
#endif
env->nip);
}
+#ifdef CONFIG_TCG
+/* Return true iff byteswap is needed to load instruction */
+static inline bool insn_need_byteswap(CPUArchState *env)
+{
+ /* SYSTEM builds TARGET_BIG_ENDIAN. Need to swap when MSR[LE] is set */
+ return !!(env->msr & ((target_ulong)1 << MSR_LE));
+}
+
+static uint32_t ppc_ldl_code(CPUArchState *env, abi_ptr addr)
+{
+ uint32_t insn = cpu_ldl_code(env, addr);
+
+ if (insn_need_byteswap(env)) {
+ insn = bswap32(insn);
+ }
+
+ return insn;
+}
+#endif
+
static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp)
{
const char *es;
return false;
}
+#ifdef CONFIG_TCG
+static bool is_prefix_insn(CPUPPCState *env, uint32_t insn)
+{
+ if (!(env->insns_flags2 & PPC2_ISA310)) {
+ return false;
+ }
+ return ((insn & 0xfc000000) == 0x04000000);
+}
+
+static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
+{
+ CPUPPCState *env = &cpu->env;
+
+ if (!tcg_enabled()) {
+ /*
+ * This does not load instructions and set the prefix bit correctly
+ * for injected interrupts with KVM. That may have to be discovered
+ * and set by the KVM layer before injecting.
+ */
+ return false;
+ }
+
+ switch (excp) {
+ case POWERPC_EXCP_HDSI:
+ /* HDSI PRTABLE_FAULT has the originating access type in error_code */
+ if ((env->spr[SPR_HDSISR] & DSISR_PRTABLE_FAULT) &&
+ (env->error_code == MMU_INST_FETCH)) {
+ /*
+ * Fetch failed due to partition scope translation, so prefix
+ * indication is not relevant (and attempting to load the
+ * instruction at NIP would cause recursive faults with the same
+ * translation).
+ */
+ break;
+ }
+ /* fall through */
+ case POWERPC_EXCP_MCHECK:
+ case POWERPC_EXCP_DSI:
+ case POWERPC_EXCP_DSEG:
+ case POWERPC_EXCP_ALIGN:
+ case POWERPC_EXCP_PROGRAM:
+ case POWERPC_EXCP_FPU:
+ case POWERPC_EXCP_TRACE:
+ case POWERPC_EXCP_HV_EMU:
+ case POWERPC_EXCP_VPU:
+ case POWERPC_EXCP_VSXU:
+ case POWERPC_EXCP_FU:
+ case POWERPC_EXCP_HV_FU: {
+ uint32_t insn = ppc_ldl_code(env, env->nip);
+ if (is_prefix_insn(env, insn)) {
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+#else
+static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
+{
+ return false;
+}
+#endif
+
static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
{
CPUState *cs = CPU(cpu);
vector |= env->excp_prefix;
+ if (is_prefix_insn_excp(cpu, excp)) {
+ msr |= PPC_BIT(34);
+ }
+
switch (excp) {
case POWERPC_EXCP_MCHECK: /* Machine check exception */
if (!FIELD_EX64(env->msr, MSR, ME)) {
vhc->hypercall(cpu->vhyp, cpu);
return;
}
+ if (env->insns_flags2 & PPC2_ISA310) {
+ /* ISAv3.1 puts LEV into SRR1 */
+ msr |= lev << 20;
+ }
if (lev == 1) {
new_msr |= (target_ulong)MSR_HVB;
}
case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */
- case POWERPC_EXCP_HV_EMU:
case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
break;
+#ifdef CONFIG_TCG
+ case POWERPC_EXCP_HV_EMU: {
+ uint32_t insn = ppc_ldl_code(env, env->nip);
+ env->spr[SPR_HEIR] = insn;
+ if (is_prefix_insn(env, insn)) {
+ uint32_t insn2 = ppc_ldl_code(env, env->nip + 4);
+ env->spr[SPR_HEIR] <<= 32;
+ env->spr[SPR_HEIR] |= insn2;
+ }
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ break;
+ }
+#endif
case POWERPC_EXCP_VPU: /* Vector unavailable exception */
case POWERPC_EXCP_VSXU: /* VSX unavailable exception */
case POWERPC_EXCP_FU: /* Facility unavailable exception */
}
/*
- * sends a message to other threads that are on the same
+ * sends a message to another thread on the same
* multi-threaded processor
*/
void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb)
{
- int pir = env->spr_cb[SPR_PIR].default_value;
+ CPUState *cs = env_cpu(env);
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
+ int ttir = rb & PPC_BITMASK(57, 63);
helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP);
- if (!dbell_type_server(rb)) {
+ if (!dbell_type_server(rb) || ttir >= nr_threads) {
return;
}
- /* TODO: TCG supports only one thread */
+ if (nr_threads == 1) {
+ ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1);
+ return;
+ }
+
+ /* Does iothread need to be locked for walking CPU list? */
+ qemu_mutex_lock_iothread();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ uint32_t thread_id = ppc_cpu_tir(ccpu);
+
+ if (ttir == thread_id) {
+ ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1);
+ qemu_mutex_unlock_iothread();
+ return;
+ }
+ }
- book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL);
+ g_assert_not_reached();
}
#endif /* TARGET_PPC64 */
/* Restore state and reload the insn we executed, for filling in DSISR. */
cpu_restore_state(cs, retaddr);
- insn = cpu_ldl_code(env, env->nip);
+ insn = ppc_ldl_code(env, env->nip);
switch (env->mmu_model) {
case POWERPC_MMU_SOFT_4xx:
unsigned int num_regs = 0;
int i;
+ for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
+ ppc_spr_t *spr = &env->spr_cb[i];
+
+ if (!spr->name) {
+ continue;
+ }
+
+ /*
+ * GDB identifies registers based on the order they are
+ * presented in the XML. These ids will not match QEMU's
+ * representation (which follows the PowerISA).
+ *
+ * Store the position of the current register description so
+ * we can make the correspondence later.
+ */
+ spr->gdb_id = num_regs;
+ num_regs++;
+ }
+
if (pcc->gdb_spr_xml) {
return;
}
g_string_append_printf(xml, " bitsize=\"%d\"", TARGET_LONG_BITS);
g_string_append(xml, " group=\"spr\"/>");
-
- /*
- * GDB identifies registers based on the order they are
- * presented in the XML. These ids will not match QEMU's
- * representation (which follows the PowerISA).
- *
- * Store the position of the current register description so
- * we can make the correspondence later.
- */
- spr->gdb_id = num_regs;
- num_regs++;
}
g_string_append(xml, "</feature>");
DEF_HELPER_2(load_dump_spr, void, env, i32)
DEF_HELPER_2(store_dump_spr, void, env, i32)
+DEF_HELPER_3(spr_write_CTRL, void, env, i32, tl)
+
DEF_HELPER_4(fscr_facility_check, void, env, i32, i32, i32)
DEF_HELPER_4(msr_facility_check, void, env, i32, i32, i32)
DEF_HELPER_FLAGS_1(load_tbl, TCG_CALL_NO_RWG, tl, env)
}
#ifdef CONFIG_DEBUG_TCG
-void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
uint32_t hflags_current = env->hflags;
uint32_t hflags_rebuilt;
.addr = (uintptr_t) &bits,
};
+ if (!kvm_enabled()) {
+ return 0;
+ }
+
return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
}
.addr = (uintptr_t) &bits,
};
+ if (!kvm_enabled()) {
+ return 0;
+ }
+
return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
}
.addr = (uintptr_t) &tcr,
};
+ if (!kvm_enabled()) {
+ return 0;
+ }
+
return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
}
env->spr[sprn]);
}
+void helper_spr_write_CTRL(CPUPPCState *env, uint32_t sprn,
+ target_ulong val)
+{
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+ uint32_t run = val & 1;
+ uint32_t ts, ts_mask;
+
+ assert(sprn == SPR_CTRL);
+
+ env->spr[sprn] &= ~1U;
+ env->spr[sprn] |= run;
+
+ ts_mask = ~(1U << (8 + env->spr[SPR_TIR]));
+ ts = run << (8 + env->spr[SPR_TIR]);
+
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
+
+ cenv->spr[sprn] &= ts_mask;
+ cenv->spr[sprn] |= ts;
+ }
+}
+
+
#ifdef TARGET_PPC64
static void raise_hv_fu_exception(CPUPPCState *env, uint32_t bit,
const char *caller, uint32_t cause,
*/
target_ulong helper_load_dpdes(CPUPPCState *env)
{
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
target_ulong dpdes = 0;
helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
- /* TODO: TCG supports only one thread */
- if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
- dpdes = 1;
+ if (nr_threads == 1) {
+ if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ dpdes = 1;
+ }
+ return dpdes;
+ }
+
+ qemu_mutex_lock_iothread();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ CPUPPCState *cenv = &ccpu->env;
+ uint32_t thread_id = ppc_cpu_tir(ccpu);
+
+ if (cenv->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ dpdes |= (0x1 << thread_id);
+ }
}
+ qemu_mutex_unlock_iothread();
return dpdes;
}
void helper_store_dpdes(CPUPPCState *env, target_ulong val)
{
PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
- /* TODO: TCG supports only one thread */
- if (val & ~0x1) {
+ if (val & ~(nr_threads - 1)) {
qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
TARGET_FMT_lx"\n", val);
+ val &= (nr_threads - 1); /* Ignore the invalid bits */
+ }
+
+ if (nr_threads == 1) {
+ ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
return;
}
- ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
+ /* Does iothread need to be locked for walking CPU list? */
+ qemu_mutex_lock_iothread();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ uint32_t thread_id = ppc_cpu_tir(ccpu);
+
+ ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & (0x1 << thread_id));
+ }
+ qemu_mutex_unlock_iothread();
}
#endif /* defined(TARGET_PPC64) */
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
+ env->error_code = 0;
+ if (cause & DSISR_PRTABLE_FAULT) {
+ /* HDSI PRTABLE_FAULT gets the originating access type in error_code */
+ env->error_code = access_type;
+ access_type = MMU_DATA_LOAD;
+ }
+
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx" 0x%"
HWADDR_PRIx" cause %08x\n",
__func__, access_str(access_type),
env->spr[SPR_HDSISR] = cause;
env->spr[SPR_HDAR] = eaddr;
env->spr[SPR_ASDR] = g_raddr;
- env->error_code = 0;
break;
default:
g_assert_not_reached();
}
static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu,
- MMUAccessType access_type,
+ MMUAccessType orig_access_type,
vaddr eaddr, hwaddr g_raddr,
ppc_v3_pate_t pate,
hwaddr *h_raddr, int *h_prot,
int *h_page_size, bool pde_addr,
int mmu_idx, bool guest_visible)
{
+ MMUAccessType access_type = orig_access_type;
int fault_cause = 0;
hwaddr pte_addr;
uint64_t pte;
+ if (pde_addr) {
+ /*
+ * Translation of process-scoped tables/directories is performed as
+ * a read-access.
+ */
+ access_type = MMU_DATA_LOAD;
+ }
+
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx
" mmu_idx %u 0x%"HWADDR_PRIx"\n",
__func__, access_str(access_type),
fault_cause |= DSISR_PRTABLE_FAULT;
}
if (guest_visible) {
- ppc_radix64_raise_hsi(cpu, access_type, eaddr, g_raddr, fault_cause);
+ ppc_radix64_raise_hsi(cpu, orig_access_type,
+ eaddr, g_raddr, fault_cause);
}
return 1;
}
* is only used to translate the effective addresses of the
* process table entries.
*/
- ret = ppc_radix64_partition_scoped_xlate(cpu, 0, eaddr, prtbe_addr,
- pate, &h_raddr, &h_prot,
- &h_page_size, true,
- /* mmu_idx is 5 because we're translating from hypervisor scope */
+ /* mmu_idx is 5 because we're translating from hypervisor scope */
+ ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr,
+ prtbe_addr, pate, &h_raddr,
+ &h_prot, &h_page_size, true,
5, guest_visible);
if (ret) {
return ret;
* translation
*/
do {
- ret = ppc_radix64_partition_scoped_xlate(cpu, 0, eaddr, pte_addr,
- pate, &h_raddr, &h_prot,
- &h_page_size, true,
/* mmu_idx is 5 because we're translating from hypervisor scope */
- 5, guest_visible);
+ ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr,
+ pte_addr, pate, &h_raddr,
+ &h_prot, &h_page_size,
+ true, 5, guest_visible);
if (ret) {
return ret;
}
void (*handler)(DisasContext *ctx);
};
+static inline bool gen_serialize(DisasContext *ctx)
+{
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return false;
+ }
+ return true;
+}
+
+#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
+static inline bool gen_serialize_core(DisasContext *ctx)
+{
+ if (ctx->flags & POWERPC_FLAG_SMT) {
+ return gen_serialize(ctx);
+ }
+
+ return true;
+}
+#endif
+
/* SPR load/store helpers */
static inline void gen_load_spr(TCGv t, int reg)
{
#endif
}
+static void spr_write_CTRL_ST(DisasContext *ctx, int sprn, int gprn)
+{
+ /* This does not implement >1 thread */
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_extract_tl(t0, cpu_gpr[gprn], 0, 1); /* Extract RUN field */
+ tcg_gen_shli_tl(t1, t0, 8); /* Duplicate the bit in TS */
+ tcg_gen_or_tl(t1, t1, t0);
+ gen_store_spr(sprn, t1);
+}
+
void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn)
{
- spr_write_generic32(ctx, sprn, gprn);
+ if (!(ctx->flags & POWERPC_FLAG_SMT)) {
+ spr_write_CTRL_ST(ctx, sprn, gprn);
+ goto out;
+ }
+
+ if (!gen_serialize(ctx)) {
+ return;
+ }
+
+ gen_helper_spr_write_CTRL(cpu_env, tcg_constant_i32(sprn),
+ cpu_gpr[gprn]);
+out:
+ spr_store_dump_spr(sprn);
/*
* SPR_CTRL writes must force a new translation block,
/* DPDES */
void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn)
{
+ if (!gen_serialize_core(ctx)) {
+ return;
+ }
+
gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env);
}
void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn)
{
+ if (!gen_serialize_core(ctx)) {
+ return;
+ }
+
gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]);
}
#endif
{
uint32_t lev;
- lev = (ctx->opcode >> 5) & 0x7F;
+ /*
+ * LEV is a 7-bit field, but the top 6 bits are treated as a reserved
+ * field (i.e., ignored). ISA v3.1 changes that to 5 bits, but that is
+ * for Ultravisor which TCG does not support, so just ignore the top 6.
+ */
+ lev = (ctx->opcode >> 5) & 0x1;
gen_exception_err(ctx, POWERPC_SYSCALL, lev);
}
return cpu->cfg.vlen >> (sew + 3 - lmul);
}
-void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *pflags);
+void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags);
void riscv_cpu_update_mask(CPURISCVState *env);
#endif
}
-void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *pflags)
+void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags)
{
CPUState *cs = env_cpu(env);
RISCVCPU *cpu = RISCV_CPU(cs);
#define RX_CPU_IRQ 0
#define RX_CPU_FIR 1
-static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPURXState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
#endif
}
-static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
if (env->psw.addr & 1) {
/*
env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T));
}
-static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUSH4State *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
/* For a gUSA region, notice the end of the region. */
#define TB_FLAG_HYPER (1 << 7)
#define TB_FLAG_ASI_SHIFT 24
-static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *pflags)
+static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags)
{
uint32_t flags;
*pc = env->pc;
void cpu_state_reset(CPUTriCoreState *s);
void tricore_tcg_init(void);
-static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
uint32_t new_flags = 0;
*pc = env->PC;
#include "exec/cpu-all.h"
-static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc,
- target_ulong *cs_base, uint32_t *flags)
+static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
TCGv_i64 tmp = tcg_temp_ebb_new_i64();
tcg_gen_dup_i64(vece, tmp, c);
- tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, c, &g);
+ tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, tmp, &g);
tcg_temp_free_i64(tmp);
}
* The requested alignment cannot overlap the TLB flags.
* FIXME: Must keep the count up-to-date with "exec/cpu-all.h".
*/
- tcg_debug_assert(a_bits + 6 <= tcg_ctx->page_bits);
+ tcg_debug_assert(a_bits + 5 <= tcg_ctx->page_bits);
#endif
}
void tcg_gen_mb(TCGBar mb_type)
{
- if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) {
+#ifdef CONFIG_USER_ONLY
+ bool parallel = tcg_ctx->gen_tb->cflags & CF_PARALLEL;
+#else
+ /*
+ * It is tempting to elide the barrier in a uniprocessor context.
+ * However, even with a single cpu we have i/o threads running in
+ * parallel, and lack of memory order can result in e.g. virtio
+ * queue entries being read incorrectly.
+ */
+ bool parallel = true;
+#endif
+
+ if (parallel) {
tcg_gen_op1(INDEX_op_mb, mb_type);
}
}
QTAILQ_REMOVE(&s->ops, op, link);
QTAILQ_INSERT_TAIL(&s->free_ops, op, link);
s->nb_ops--;
-
-#ifdef CONFIG_PROFILER
- qatomic_set(&s->prof.del_op_count, s->prof.del_op_count + 1);
-#endif
}
void tcg_remove_ops_after(TCGOp *op)
tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg);
}
-#ifdef CONFIG_PROFILER
-
-/* avoid copy/paste errors */
-#define PROF_ADD(to, from, field) \
- do { \
- (to)->field += qatomic_read(&((from)->field)); \
- } while (0)
-
-#define PROF_MAX(to, from, field) \
- do { \
- typeof((from)->field) val__ = qatomic_read(&((from)->field)); \
- if (val__ > (to)->field) { \
- (to)->field = val__; \
- } \
- } while (0)
-
-/* Pass in a zero'ed @prof */
-static inline
-void tcg_profile_snapshot(TCGProfile *prof, bool counters, bool table)
-{
- unsigned int n_ctxs = qatomic_read(&tcg_cur_ctxs);
- unsigned int i;
-
- for (i = 0; i < n_ctxs; i++) {
- TCGContext *s = qatomic_read(&tcg_ctxs[i]);
- const TCGProfile *orig = &s->prof;
-
- if (counters) {
- PROF_ADD(prof, orig, cpu_exec_time);
- PROF_ADD(prof, orig, tb_count1);
- PROF_ADD(prof, orig, tb_count);
- PROF_ADD(prof, orig, op_count);
- PROF_MAX(prof, orig, op_count_max);
- PROF_ADD(prof, orig, temp_count);
- PROF_MAX(prof, orig, temp_count_max);
- PROF_ADD(prof, orig, del_op_count);
- PROF_ADD(prof, orig, code_in_len);
- PROF_ADD(prof, orig, code_out_len);
- PROF_ADD(prof, orig, search_out_len);
- PROF_ADD(prof, orig, interm_time);
- PROF_ADD(prof, orig, code_time);
- PROF_ADD(prof, orig, la_time);
- PROF_ADD(prof, orig, opt_time);
- PROF_ADD(prof, orig, restore_count);
- PROF_ADD(prof, orig, restore_time);
- }
- if (table) {
- int i;
-
- for (i = 0; i < NB_OPS; i++) {
- PROF_ADD(prof, orig, table_op_count[i]);
- }
- }
- }
-}
-
-#undef PROF_ADD
-#undef PROF_MAX
-
-static void tcg_profile_snapshot_counters(TCGProfile *prof)
-{
- tcg_profile_snapshot(prof, true, false);
-}
-
-static void tcg_profile_snapshot_table(TCGProfile *prof)
-{
- tcg_profile_snapshot(prof, false, true);
-}
-
-void tcg_dump_op_count(GString *buf)
-{
- TCGProfile prof = {};
- int i;
-
- tcg_profile_snapshot_table(&prof);
- for (i = 0; i < NB_OPS; i++) {
- g_string_append_printf(buf, "%s %" PRId64 "\n", tcg_op_defs[i].name,
- prof.table_op_count[i]);
- }
-}
-
-int64_t tcg_cpu_exec_time(void)
-{
- unsigned int n_ctxs = qatomic_read(&tcg_cur_ctxs);
- unsigned int i;
- int64_t ret = 0;
-
- for (i = 0; i < n_ctxs; i++) {
- const TCGContext *s = qatomic_read(&tcg_ctxs[i]);
- const TCGProfile *prof = &s->prof;
-
- ret += qatomic_read(&prof->cpu_exec_time);
- }
- return ret;
-}
-#else
void tcg_dump_op_count(GString *buf)
{
g_string_append_printf(buf, "[TCG profiler not compiled]\n");
}
-int64_t tcg_cpu_exec_time(void)
-{
- error_report("%s: TCG profiler not compiled", __func__);
- exit(EXIT_FAILURE);
-}
-#endif
-
-
int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start)
{
-#ifdef CONFIG_PROFILER
- TCGProfile *prof = &s->prof;
-#endif
int i, start_words, num_insns;
TCGOp *op;
-#ifdef CONFIG_PROFILER
- {
- int n = 0;
-
- QTAILQ_FOREACH(op, &s->ops, link) {
- n++;
- }
- qatomic_set(&prof->op_count, prof->op_count + n);
- if (n > prof->op_count_max) {
- qatomic_set(&prof->op_count_max, n);
- }
-
- n = s->nb_temps;
- qatomic_set(&prof->temp_count, prof->temp_count + n);
- if (n > prof->temp_count_max) {
- qatomic_set(&prof->temp_count_max, n);
- }
- }
-#endif
-
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)
&& qemu_log_in_addr_range(pc_start))) {
FILE *logfile = qemu_log_trylock();
}
#endif
-#ifdef CONFIG_PROFILER
- qatomic_set(&prof->opt_time, prof->opt_time - profile_getclock());
-#endif
-
tcg_optimize(s);
-#ifdef CONFIG_PROFILER
- qatomic_set(&prof->opt_time, prof->opt_time + profile_getclock());
- qatomic_set(&prof->la_time, prof->la_time - profile_getclock());
-#endif
-
reachable_code_pass(s);
liveness_pass_0(s);
liveness_pass_1(s);
}
}
-#ifdef CONFIG_PROFILER
- qatomic_set(&prof->la_time, prof->la_time + profile_getclock());
-#endif
-
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT)
&& qemu_log_in_addr_range(pc_start))) {
FILE *logfile = qemu_log_trylock();
QTAILQ_FOREACH(op, &s->ops, link) {
TCGOpcode opc = op->opc;
-#ifdef CONFIG_PROFILER
- qatomic_set(&prof->table_op_count[opc], prof->table_op_count[opc] + 1);
-#endif
-
switch (opc) {
case INDEX_op_mov_i32:
case INDEX_op_mov_i64:
return tcg_current_code_size(s);
}
-#ifdef CONFIG_PROFILER
-void tcg_dump_info(GString *buf)
-{
- TCGProfile prof = {};
- const TCGProfile *s;
- int64_t tb_count;
- int64_t tb_div_count;
- int64_t tot;
-
- tcg_profile_snapshot_counters(&prof);
- s = &prof;
- tb_count = s->tb_count;
- tb_div_count = tb_count ? tb_count : 1;
- tot = s->interm_time + s->code_time;
-
- g_string_append_printf(buf, "JIT cycles %" PRId64
- " (%0.3f s at 2.4 GHz)\n",
- tot, tot / 2.4e9);
- g_string_append_printf(buf, "translated TBs %" PRId64
- " (aborted=%" PRId64 " %0.1f%%)\n",
- tb_count, s->tb_count1 - tb_count,
- (double)(s->tb_count1 - s->tb_count)
- / (s->tb_count1 ? s->tb_count1 : 1) * 100.0);
- g_string_append_printf(buf, "avg ops/TB %0.1f max=%d\n",
- (double)s->op_count / tb_div_count, s->op_count_max);
- g_string_append_printf(buf, "deleted ops/TB %0.2f\n",
- (double)s->del_op_count / tb_div_count);
- g_string_append_printf(buf, "avg temps/TB %0.2f max=%d\n",
- (double)s->temp_count / tb_div_count,
- s->temp_count_max);
- g_string_append_printf(buf, "avg host code/TB %0.1f\n",
- (double)s->code_out_len / tb_div_count);
- g_string_append_printf(buf, "avg search data/TB %0.1f\n",
- (double)s->search_out_len / tb_div_count);
-
- g_string_append_printf(buf, "cycles/op %0.1f\n",
- s->op_count ? (double)tot / s->op_count : 0);
- g_string_append_printf(buf, "cycles/in byte %0.1f\n",
- s->code_in_len ? (double)tot / s->code_in_len : 0);
- g_string_append_printf(buf, "cycles/out byte %0.1f\n",
- s->code_out_len ? (double)tot / s->code_out_len : 0);
- g_string_append_printf(buf, "cycles/search byte %0.1f\n",
- s->search_out_len ?
- (double)tot / s->search_out_len : 0);
- if (tot == 0) {
- tot = 1;
- }
- g_string_append_printf(buf, " gen_interm time %0.1f%%\n",
- (double)s->interm_time / tot * 100.0);
- g_string_append_printf(buf, " gen_code time %0.1f%%\n",
- (double)s->code_time / tot * 100.0);
- g_string_append_printf(buf, "optim./code time %0.1f%%\n",
- (double)s->opt_time / (s->code_time ?
- s->code_time : 1)
- * 100.0);
- g_string_append_printf(buf, "liveness/code time %0.1f%%\n",
- (double)s->la_time / (s->code_time ?
- s->code_time : 1) * 100.0);
- g_string_append_printf(buf, "cpu_restore count %" PRId64 "\n",
- s->restore_count);
- g_string_append_printf(buf, " avg cycles %0.1f\n",
- s->restore_count ?
- (double)s->restore_time / s->restore_count : 0);
-}
-#else
void tcg_dump_info(GString *buf)
{
g_string_append_printf(buf, "[TCG profiler not compiled]\n");
}
-#endif
#ifdef ELF_HOST_MACHINE
/* In order to use this feature, the backend needs to do three things:
timeout = 90
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
panic_message = 'Kernel panic - not syncing'
+ good_message = 'VFS: Cannot open root device'
- def test_ppc64_pseries(self):
- """
- :avocado: tags=arch:ppc64
- :avocado: tags=machine:pseries
- """
+ def do_test_ppc64_linux_boot(self):
kernel_url = ('https://archives.fedoraproject.org/pub/archive'
'/fedora-secondary/releases/29/Everything/ppc64le/os'
'/ppc/ppc64/vmlinuz')
self.vm.add_args('-kernel', kernel_path,
'-append', kernel_command_line)
self.vm.launch()
- console_pattern = 'Kernel command line: %s' % kernel_command_line
+
+ def test_ppc64_vof_linux_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-machine', 'x-vof=on')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+
+ def test_ppc64_linux_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+
+ def test_ppc64_linux_smp_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '4')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_ppc64_linux_smt_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '4,threads=4')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_ppc64_linux_big_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2')
+ self.vm.add_args('-m', '512M',
+ '-object', 'memory-backend-ram,size=256M,id=m0',
+ '-object', 'memory-backend-ram,size=256M,id=m1')
+ self.vm.add_args('-numa', 'node,nodeid=0,memdev=m0')
+ self.vm.add_args('-numa', 'node,nodeid=1,memdev=m1')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 2 nodes, 16 CPUs'
wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
#define MAX_CPUS 8 /* lets not go nuts */
typedef struct {
- uint64_t last_pc;
uint64_t insn_count;
} InstructionCount;
{
unsigned int i = cpu_index % MAX_CPUS;
InstructionCount *c = &counts[i];
- uint64_t this_pc = GPOINTER_TO_UINT(udata);
- if (this_pc == c->last_pc) {
- g_autofree gchar *out = g_strdup_printf("detected repeat execution @ 0x%"
- PRIx64 "\n", this_pc);
- qemu_plugin_outs(out);
- }
- c->last_pc = this_pc;
+
c->insn_count++;
}
qtest_start(cmdline->str);
qtest_end();
+ rmdir(tmpfs);
}
static void cxl_t3d_persistent(void)
qtest_start(cmdline->str);
qtest_end();
+ rmdir(tmpfs);
}
static void cxl_t3d_volatile(void)
#include "qemu/osdep.h"
+#include "qemu/sockets.h"
#include "qemu/dbus.h"
#include "qemu/sockets.h"
#include <gio/gio.h>
g_autoptr(GSocketConnection) socketc = NULL;
GDBusConnection *conn;
+#ifdef WIN32
+ socket = g_socket_new_from_fd(_get_osfhandle(fd), &err);
+#else
socket = g_socket_new_from_fd(fd, &err);
+#endif
g_assert_no_error(err);
socketc = g_socket_connection_factory_create_connection(socket);
qemu_dbus_display1_console_call_register_listener_finish(
QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
- NULL, res, &err);
+#ifndef WIN32
+ NULL,
+#endif
+ res, &err);
g_assert_no_error(err);
test->listener_conn = g_thread_join(test->thread);
g_autoptr(GError) err = NULL;
g_autoptr(GDBusConnection) conn = NULL;
g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
- g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GMainLoop) loop = NULL;
QTestState *qts = NULL;
- int pair[2], idx;
+ int pair[2];
TestDBusConsoleRegister test;
+#ifdef WIN32
+ WSAPROTOCOL_INFOW info;
+ g_autoptr(GVariant) listener = NULL;
+#else
+ g_autoptr(GUnixFDList) fd_list = NULL;
+ int idx;
+#endif
test_setup(&qts, &conn);
g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
+#ifndef WIN32
fd_list = g_unix_fd_list_new();
idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
+#endif
console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
qemu_dbus_display1_console_proxy_new_sync(
test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
GINT_TO_POINTER(pair[0]));
+#ifdef WIN32
+ if (WSADuplicateSocketW(_get_osfhandle(pair[1]),
+ GetProcessId((HANDLE) qtest_pid(qts)),
+ &info) == SOCKET_ERROR)
+ {
+ g_autofree char *emsg = g_win32_error_message(WSAGetLastError());
+ g_error("WSADuplicateSocket failed: %s", emsg);
+ }
+ close(pair[1]);
+ listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ &info,
+ sizeof(info),
+ 1);
+#endif
+
qemu_dbus_display1_console_call_register_listener(
QEMU_DBUS_DISPLAY1_CONSOLE(console),
+#ifdef WIN32
+ listener,
+#else
g_variant_new_handle(idx),
+#endif
G_DBUS_CALL_FLAGS_NONE,
-1,
+#ifndef WIN32
fd_list,
+#endif
NULL,
test_dbus_console_registered,
&test);
return ret;
}
+pid_t qtest_pid(QTestState *s)
+{
+ return s->qemu_pid;
+}
+
bool qtest_probe_child(QTestState *s)
{
pid_t pid = s->qemu_pid;
* Returns: Value retrieved from property.
*/
bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property);
+
+/**
+ * qtest_pid:
+ * @s: QTestState instance to operate on.
+ *
+ * Returns: the PID of the QEMU process, or <= 0
+ */
+pid_t qtest_pid(QTestState *s);
+
#endif
'numa-test'
]
-if dbus_display and targetos != 'windows'
+if dbus_display
qtests_i386 += ['dbus-display-test']
endif
{ "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE },
{ "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR },
{ "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR },
-#ifndef CONFIG_PROFILER
- { "x-query-profile", ERROR_CLASS_GENERIC_ERROR },
-#endif
/* Only valid with a USB bus added */
{ "x-query-usb", ERROR_CLASS_GENERIC_ERROR },
/* Only valid with accel=tcg */
/* iterate all regions */
for (i = 0; i < s->fds_num; i++) {
- /* We'll check only the region statring at 0x0*/
+ /* We'll check only the region starting at 0x0 */
if (s->memory.regions[i].guest_phys_addr != 0x0) {
continue;
}
memory: CFLAGS+=-DCHECK_UNALIGNED=1
-# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so
-run-plugin-%-with-libinsn.so:
- $(call run-test, $@, \
- $(QEMU) -monitor none -display none \
- -chardev file$(COMMA)path=$@.out$(COMMA)id=output \
- -plugin ../../plugin/libinsn.so$(COMMA)inline=on \
- -d plugin -D $*-with-libinsn.so.pout \
- $(QEMU_OPTS) $*)
-
# Running
QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel
SKIP_I386_TESTS+=test-i386-fprem
endif
-# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so
-run-plugin-%-with-libinsn.so:
- $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
- -plugin ../../plugin/libinsn.so$(COMMA)inline=on \
- -d plugin -D $*-with-libinsn.so.pout $*)
-
# Update TESTS
I386_TESTS:=$(filter-out $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
TESTS=$(MULTIARCH_TESTS) $(I386_TESTS)
memory: CFLAGS+=-DCHECK_UNALIGNED=1
-# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so
-run-plugin-%-with-libinsn.so:
- $(call run-test, $@, \
- $(QEMU) -monitor none -display none \
- -chardev file$(COMMA)path=$@.out$(COMMA)id=output \
- -plugin ../../plugin/libinsn.so$(COMMA)inline=on \
- -d plugin -D $*-with-libinsn.so.pout \
- $(QEMU_OPTS) $*)
-
# Running
QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel
con->scanout.texture.x,
con->scanout.texture.y,
con->scanout.texture.width,
- con->scanout.texture.height);
+ con->scanout.texture.height,
+ con->scanout.texture.d3d_tex2d);
}
}
return s;
}
+#ifdef WIN32
+void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
+ HANDLE h, uint32_t offset)
+{
+ assert(!surface->handle);
+
+ surface->handle = h;
+ surface->handle_offset = offset;
+}
+
+static void
+win32_pixman_image_destroy(pixman_image_t *image, void *data)
+{
+ DisplaySurface *surface = data;
+
+ if (!surface->handle) {
+ return;
+ }
+
+ assert(surface->handle_offset == 0);
+
+ qemu_win32_map_free(
+ pixman_image_get_data(surface->image),
+ surface->handle,
+ &error_warn
+ );
+}
+#endif
+
DisplaySurface *qemu_create_displaysurface(int width, int height)
{
- DisplaySurface *surface = g_new0(DisplaySurface, 1);
+ DisplaySurface *surface;
+ void *bits = NULL;
+#ifdef WIN32
+ HANDLE handle = NULL;
+#endif
- trace_displaysurface_create(surface, width, height);
- surface->format = PIXMAN_x8r8g8b8;
- surface->image = pixman_image_create_bits(surface->format,
- width, height,
- NULL, width * 4);
- assert(surface->image != NULL);
+ trace_displaysurface_create(width, height);
+
+#ifdef WIN32
+ bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
+#endif
+
+ surface = qemu_create_displaysurface_from(
+ width, height,
+ PIXMAN_x8r8g8b8,
+ width * 4, bits
+ );
surface->flags = QEMU_ALLOCATED_FLAG;
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(surface, handle, 0);
+#endif
return surface;
}
width, height,
(void *)data, linesize);
assert(surface->image != NULL);
+#ifdef WIN32
+ pixman_image_set_destroy_function(surface->image,
+ win32_pixman_image_destroy, surface);
+#endif
return surface;
}
return true;
}
+void console_handle_touch_event(QemuConsole *con,
+ struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
+ uint64_t num_slot,
+ int width, int height,
+ double x, double y,
+ InputMultiTouchType type,
+ Error **errp)
+{
+ struct touch_slot *slot;
+ bool needs_sync = false;
+ int update;
+ int i;
+
+ if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
+ error_setg(errp,
+ "Unexpected touch slot number: % " PRId64" >= %d",
+ num_slot, INPUT_EVENT_SLOTS_MAX);
+ return;
+ }
+
+ slot = &touch_slots[num_slot];
+ slot->x = x;
+ slot->y = y;
+
+ if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
+ slot->tracking_id = num_slot;
+ }
+
+ for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
+ if (i == num_slot) {
+ update = type;
+ } else {
+ update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
+ }
+
+ slot = &touch_slots[i];
+
+ if (slot->tracking_id == -1) {
+ continue;
+ }
+
+ if (update == INPUT_MULTI_TOUCH_TYPE_END) {
+ slot->tracking_id = -1;
+ qemu_input_queue_mtt(con, update, i, slot->tracking_id);
+ needs_sync = true;
+ } else {
+ qemu_input_queue_mtt(con, update, i, slot->tracking_id);
+ qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
+ qemu_input_queue_mtt_abs(con,
+ INPUT_AXIS_X, (int) slot->x,
+ 0, width,
+ i, slot->tracking_id);
+ qemu_input_queue_mtt_abs(con,
+ INPUT_AXIS_Y, (int) slot->y,
+ 0, height,
+ i, slot->tracking_id);
+ needs_sync = true;
+ }
+ }
+
+ if (needs_sync) {
+ qemu_input_event_sync();
+ }
+}
+
void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
{
/* display has opengl support */
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t width, uint32_t height)
+ uint32_t width, uint32_t height,
+ void *d3d_tex2d)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
con->scanout.kind = SCANOUT_TEXTURE;
con->scanout.texture = (ScanoutTexture) {
backing_id, backing_y_0_top, backing_width, backing_height,
- x, y, width, height
+ x, y, width, height, d3d_tex2d,
};
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != (dcl->con ? dcl->con : active_console)) {
dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
backing_y_0_top,
backing_width, backing_height,
- x, y, width, height);
+ x, y, width, height,
+ d3d_tex2d);
}
}
}
if (con == NULL) {
con = active_console;
}
- return con->cursor;
+ return con ? con->cursor : NULL;
}
bool qemu_console_is_visible(QemuConsole *con)
#include "qemu/config-file.h"
#include "qemu/option.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
#include "dbus.h"
dbus_chr_register(
DBusChardev *dc,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_stream,
QemuDBusDisplay1Chardev *object)
{
g_autoptr(GError) err = NULL;
int fd;
+#ifdef G_OS_WIN32
+ if (!dbus_win32_import_socket(invocation, arg_stream, &fd)) {
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+#else
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_stream), &err);
if (err) {
g_dbus_method_invocation_return_error(
"Couldn't get peer FD: %s", err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
if (qemu_chr_add_client(CHARDEV(dc), fd) < 0) {
g_dbus_method_invocation_return_error(invocation,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't register FD!");
+#ifdef G_OS_WIN32
+ closesocket(fd);
+#else
close(fd);
+#endif
return DBUS_METHOD_INVOCATION_HANDLED;
}
"owner", g_dbus_method_invocation_get_sender(invocation),
NULL);
- qemu_dbus_display1_chardev_complete_register(object, invocation, NULL);
+ qemu_dbus_display1_chardev_complete_register(object, invocation
+#ifndef G_OS_WIN32
+ , NULL
+#endif
+ );
return DBUS_METHOD_INVOCATION_HANDLED;
}
#include "ui/kbd-state.h"
#include "trace.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
#include "dbus.h"
+static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX];
+
struct _DBusDisplayConsole {
GDBusObjectSkeleton parent_instance;
DisplayChangeListener dcl;
QKbdState *kbd;
QemuDBusDisplay1Mouse *iface_mouse;
+ QemuDBusDisplay1MultiTouch *iface_touch;
gboolean last_set;
guint last_x;
guint last_y;
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifdef G_OS_WIN32
+bool
+dbus_win32_import_socket(GDBusMethodInvocation *invocation,
+ GVariant *arg_listener, int *socket)
+{
+ gsize n;
+ WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1);
+
+ if (!info || n != sizeof(*info)) {
+ g_dbus_method_invocation_return_error(
+ invocation,
+ DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_FAILED,
+ "Failed to get socket infos");
+ return false;
+ }
+
+ *socket = WSASocketW(FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ info, 0, 0);
+ if (*socket == INVALID_SOCKET) {
+ g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError());
+ g_dbus_method_invocation_return_error(
+ invocation,
+ DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_FAILED,
+ "Couldn't create socket: %s", emsg);
+ return false;
+ }
+
+ return true;
+}
+#endif
+
static gboolean
dbus_console_register_listener(DBusDisplayConsole *ddc,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener)
{
const char *sender = g_dbus_method_invocation_get_sender(invocation);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifdef G_OS_WIN32
+ if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+#else
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
if (err) {
g_dbus_method_invocation_return_error(
"Couldn't get peer fd: %s", err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
socket = g_socket_new_from_fd(fd, &err);
if (err) {
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't make a socket: %s", err->message);
+#ifdef G_OS_WIN32
+ closesocket(fd);
+#else
close(fd);
+#endif
return DBUS_METHOD_INVOCATION_HANDLED;
}
socket_conn = g_socket_connection_factory_create_connection(socket);
qemu_dbus_display1_console_complete_register_listener(
- ddc->iface, invocation, NULL);
+ ddc->iface, invocation
+#ifdef G_OS_UNIX
+ , NULL
+#endif
+ );
listener_conn = g_dbus_connection_new_sync(
G_IO_STREAM(socket_conn),
return DBUS_METHOD_INVOCATION_HANDLED;
}
+static gboolean
+dbus_touch_send_event(DBusDisplayConsole *ddc,
+ GDBusMethodInvocation *invocation,
+ guint kind, uint64_t num_slot,
+ double x, double y)
+{
+ Error *error = NULL;
+ int width, height;
+ trace_dbus_touch_send_event(kind, num_slot, x, y);
+
+ if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN &&
+ kind != INPUT_MULTI_TOUCH_TYPE_UPDATE &&
+ kind != INPUT_MULTI_TOUCH_TYPE_CANCEL &&
+ kind != INPUT_MULTI_TOUCH_TYPE_END)
+ {
+ g_dbus_method_invocation_return_error(
+ invocation, DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_INVALID,
+ "Invalid touch event kind");
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+ width = qemu_console_get_width(ddc->dcl.con, 0);
+ height = qemu_console_get_height(ddc->dcl.con, 0);
+
+ console_handle_touch_event(ddc->dcl.con, touch_slots,
+ num_slot, width, height,
+ x, y, kind, &error);
+ if (error != NULL) {
+ g_dbus_method_invocation_return_error(
+ invocation, DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_INVALID,
+ error_get_pretty(error), NULL);
+ error_free(error);
+ } else {
+ qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch,
+ invocation);
+ }
+ return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
static gboolean
dbus_mouse_set_pos(DBusDisplayConsole *ddc,
GDBusMethodInvocation *invocation,
g_autofree char *label = NULL;
char device_addr[256] = "";
DBusDisplayConsole *ddc;
- int idx;
+ int idx, i;
+ const char *interfaces[] = {
+ "org.qemu.Display1.Keyboard",
+ "org.qemu.Display1.Mouse",
+ "org.qemu.Display1.MultiTouch",
+ NULL
+ };
assert(display);
assert(con);
"width", qemu_console_get_width(con, 0),
"height", qemu_console_get_height(con, 0),
"device-address", device_addr,
+ "interfaces", interfaces,
NULL);
g_object_connect(ddc->iface,
"swapped-signal::handle-register-listener",
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse));
+ ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new();
+ g_object_connect(ddc->iface_touch,
+ "swapped-signal::handle-send-event", dbus_touch_send_event, ddc,
+ NULL);
+ qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch,
+ INPUT_EVENT_SLOTS_MAX);
+ g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
+ G_DBUS_INTERFACE_SKELETON(ddc->iface_touch));
+
+ for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) {
+ struct touch_slot *slot = &touch_slots[i];
+ slot->tracking_id = -1;
+ }
+
register_displaychangelistener(&ddc->dcl);
ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
The list of consoles available on ``/org/qemu/Display1/Console_$id``.
-->
<property name="ConsoleIDs" type="au" access="read"/>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/VM object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
"Text" (see :dbus:prop:`Type` and other properties).
Interactions with a console may be done with
- :dbus:iface:`org.qemu.Display1.Keyboard` and
- :dbus:iface:`org.qemu.Display1.Mouse` interfaces when available.
+ :dbus:iface:`org.qemu.Display1.Keyboard`,
+ :dbus:iface:`org.qemu.Display1.Mouse` and
+ :dbus:iface:`org.qemu.Display1.MultiTouch` interfaces when available.
-->
<interface name="org.qemu.Display1.Console">
<!--
:dbus:iface:`org.qemu.Display1.Listener` interface.
-->
<method name="RegisterListener">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="listener" direction="in"/>
+ <?endif?>
</method>
<!--
The device address (ex: "pci/0000/02.0").
-->
<property name="DeviceAddress" type="s" access="read"/>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ ``/org/qemu/Display1/Console_$id`` object, and can be used to detect the
+ capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
org.qemu.Display1.Keyboard:
- This interface in implemented on ``/org/qemu/Display1/Console_$id`` (see
+ This interface is optionally implemented on
+ ``/org/qemu/Display1/Console_$id`` (see
:dbus:iface:`~org.qemu.Display1.Console`).
-->
<interface name="org.qemu.Display1.Keyboard">
<!--
org.qemu.Display1.Mouse:
- This interface in implemented on ``/org/qemu/Display1/Console_$id`` (see
+ This interface is optionally implemented on
+ ``/org/qemu/Display1/Console_$id`` (see
:dbus:iface:`~org.qemu.Display1.Console` documentation).
.. _dbus-button-values:
<property name="IsAbsolute" type="b" access="read"/>
</interface>
+ <!--
+ org.qemu.Display1.MultiTouch:
+
+ This interface in implemented on ``/org/qemu/Display1/Console_$id`` (see
+ :dbus:iface:`~org.qemu.Display1.Console` documentation).
+
+ .. _dbus-kind-values:
+
+ **Kind values**::
+
+ Begin = 0
+ Update = 1
+ End = 2
+ Cancel = 3
+ -->
+ <interface name="org.qemu.Display1.MultiTouch">
+ <!--
+ SendEvent:
+ @kind: The touch event kind
+ @num_slot: The slot number.
+ @x: The x coordinates.
+ @y: The y coordinates.
+
+ Send a touch gesture event.
+ -->
+ <method name="SendEvent">
+ <arg type="u" name="kind" direction="in"/>
+ <arg type="t" name="num_slot" direction="in"/>
+ <arg type="d" name="x" direction="in"/>
+ <arg type="d" name="y" direction="in"/>
+ </method>
+
+ <!--
+ MaxSlots:
+
+ The maximum number of slots.
+ -->
+ <property name="MaxSlots" type="i" access="read"/>
+ </interface>
+
<!--
org.qemu.Display1.Listener:
</arg>
</method>
+ <?if $(env.TARGETOS) != windows?>
<!--
ScanoutDMABUF:
@dmabuf: the DMABUF file descriptor.
<arg type="i" name="width" direction="in"/>
<arg type="i" name="height" direction="in"/>
</method>
+ <?endif?>
<!--
Disable:
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/Listener object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
+ </interface>
+
+ <!--
+ org.qemu.Display1.Listener.Win32.Map:
+
+ This optional client-side interface can complement
+ org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for Windows
+ specific shared memory scanouts.
+ -->
+ <interface name="org.qemu.Display1.Listener.Win32.Map">
+ <!--
+ ScanoutMap:
+ @handle: the shared map handle value.
+ @offset: mapping offset.
+ @width: display width, in pixels.
+ @height: display height, in pixels.
+ @stride: stride, in bytes.
+ @pixman_format: image format (ex: ``PIXMAN_X8R8G8B8``).
+
+ Resize and update the display content with a shared map.
+ -->
+ <method name="ScanoutMap">
+ <arg type="t" name="handle" direction="in"/>
+ <arg type="u" name="offset" direction="in"/>
+ <arg type="u" name="width" direction="in"/>
+ <arg type="u" name="height" direction="in"/>
+ <arg type="u" name="stride" direction="in"/>
+ <arg type="u" name="pixman_format" direction="in"/>
+ </method>
+
+ <!--
+ UpdateMap:
+ @x: the X update position, in pixels.
+ @y: the Y update position, in pixels.
+ @width: the update width, in pixels.
+ @height: the update height, in pixels.
+
+ Update the display content with the current shared map and the given region.
+ -->
+ <method name="UpdateMap">
+ <arg type="i" name="x" direction="in"/>
+ <arg type="i" name="y" direction="in"/>
+ <arg type="i" name="width" direction="in"/>
+ <arg type="i" name="height" direction="in"/>
+ </method>
+ </interface>
+
+ <!--
+ org.qemu.Display1.Listener.Win32.D3d11:
+
+ This optional client-side interface can complement
+ org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for Windows
+ specific Direct3D texture sharing of the scanouts.
+ -->
+ <interface name="org.qemu.Display1.Listener.Win32.D3d11">
+ <!--
+ ScanoutTexture2d:
+ @handle: the NT handle for the shared texture (to be opened back with ID3D11Device1::OpenSharedResource1).
+ @texture_width: texture width, in pixels.
+ @texture_height: texture height, in pixels.
+ @y0_top: whether Y position 0 is the top or not.
+ @x: the X scanout position, in pixels.
+ @y: the Y scanout position, in pixels.
+ @width: the scanout width, in pixels.
+ @height: the scanout height, in pixels.
+
+ Resize and update the display content with a Direct3D 11 2D texture.
+ You must acquire and release the associated KeyedMutex 0 during rendering.
+ -->
+ <method name="ScanoutTexture2d">
+ <arg type="t" name="handle" direction="in"/>
+ <arg type="u" name="texture_width" direction="in"/>
+ <arg type="u" name="texture_height" direction="in"/>
+ <arg type="b" name="y0_top" direction="in"/>
+ <arg type="u" name="x" direction="in"/>
+ <arg type="u" name="y" direction="in"/>
+ <arg type="u" name="width" direction="in"/>
+ <arg type="u" name="height" direction="in"/>
+ </method>
+
+ <!--
+ UpdateTexture2d:
+ @x: the X update position, in pixels.
+ @y: the Y update position, in pixels.
+ @width: the update width, in pixels.
+ @height: the update height, in pixels.
+
+ Update the display content with the current Direct3D 2D texture and the given region.
+ You must acquire and release the associated KeyedMutex 0 during rendering.
+ -->
+ <method name="UpdateTexture2d">
+ <arg type="i" name="x" direction="in"/>
+ <arg type="i" name="y" direction="in"/>
+ <arg type="i" name="width" direction="in"/>
+ <arg type="i" name="height" direction="in"/>
+ </method>
</interface>
<!--
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/Clipboard object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
:dbus:iface:`org.qemu.Display1.AudioOutListener` interface.
-->
<method name="RegisterOutListener">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="listener" direction="in"/>
+ <?endif?>
</method>
<!--
:dbus:iface:`org.qemu.Display1.AudioInListener` interface.
-->
<method name="RegisterInListener">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="listener" direction="in"/>
+ <?endif?>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/Audio object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/AudioOutListener object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/AudioInListener object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
The current handler, if any, will be replaced.
-->
<method name="Register">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="stream" direction="in"/>
+ <?endif?>
</method>
<!--
The D-Bus unique name of the registered handler.
-->
<property name="Owner" type="s" access="read"/>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ ``/org/qemu/Display1/Chardev_$i`` object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
</node>
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "dbus.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
+#ifdef WIN32
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#endif
#ifdef CONFIG_OPENGL
#include "ui/shader.h"
#endif
#include "trace.h"
+static void dbus_gfx_switch(DisplayChangeListener *dcl,
+ struct DisplaySurface *new_surface);
+
+enum share_kind {
+ SHARE_KIND_NONE,
+ SHARE_KIND_MAPPED,
+ SHARE_KIND_D3DTEX,
+};
+
struct _DBusDisplayListener {
GObject parent;
DisplayChangeListener dcl;
DisplaySurface *ds;
+ enum share_kind ds_share;
+
int gl_updates;
+
+ bool ds_mapped;
+ bool can_share_map;
+
+#ifdef WIN32
+ QemuDBusDisplay1ListenerWin32Map *map_proxy;
+ QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy;
+ HANDLE peer_process;
+ ID3D11Texture2D *d3d_texture;
+#ifdef CONFIG_OPENGL
+ egl_fb fb;
+#endif
+#endif
};
G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+static void dbus_gfx_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h);
+
+#ifdef CONFIG_OPENGL
+static void dbus_scanout_disable(DisplayChangeListener *dcl)
+{
+ DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+
+ qemu_dbus_display1_listener_call_disable(
+ ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+#ifdef WIN32
+static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture,
+ HANDLE *handle, Error **errp)
+{
+ IDXGIResource1 *dxgiResource = NULL;
+ HRESULT hr;
+
+ hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
+ &IID_IDXGIResource1,
+ (void **)&dxgiResource);
+ if (FAILED(hr)) {
+ goto fail;
+ }
+
+ hr = dxgiResource->lpVtbl->CreateSharedHandle(
+ dxgiResource,
+ NULL,
+ DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
+ NULL,
+ handle
+ );
+
+ dxgiResource->lpVtbl->Release(dxgiResource);
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+
+fail:
+ error_setg_win32(errp, GetLastError(), "failed to create shared handle");
+ return false;
+}
+
+static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp)
+{
+ IDXGIKeyedMutex *dxgiMutex = NULL;
+ HRESULT hr;
+
+ hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
+ &IID_IDXGIKeyedMutex,
+ (void **)&dxgiMutex);
+ if (FAILED(hr)) {
+ goto fail;
+ }
+
+ hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE);
+
+ dxgiMutex->lpVtbl->Release(dxgiMutex);
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+
+fail:
+ error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex");
+ return false;
+}
+
+static bool d3d_texture2d_release0(ID3D11Texture2D *d3d_texture, Error **errp)
+{
+ IDXGIKeyedMutex *dxgiMutex = NULL;
+ HRESULT hr;
+
+ hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
+ &IID_IDXGIKeyedMutex,
+ (void **)&dxgiMutex);
+ if (FAILED(hr)) {
+ goto fail;
+ }
+
+ hr = dxgiMutex->lpVtbl->ReleaseSync(dxgiMutex, 0);
+
+ dxgiMutex->lpVtbl->Release(dxgiMutex);
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+
+fail:
+ error_setg_win32(errp, GetLastError(), "failed to release texture mutex");
+ return false;
+}
+#endif /* WIN32 */
+
static void dbus_update_gl_cb(GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+ GAsyncResult *res,
+ gpointer user_data)
{
g_autoptr(GError) err = NULL;
DBusDisplayListener *ddl = user_data;
+ bool success;
+
+#ifdef CONFIG_GBM
+ success = qemu_dbus_display1_listener_call_update_dmabuf_finish(
+ ddl->proxy, res, &err);
+#endif
+
+#ifdef WIN32
+ success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish(
+ ddl->d3d11_proxy, res, &err);
+ d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn);
+#endif
- if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddl->proxy,
- res, &err)) {
+ if (!success) {
error_report("Failed to call update: %s", err->message);
}
g_object_unref(ddl);
}
-static void dbus_call_update_gl(DBusDisplayListener *ddl,
+static void dbus_call_update_gl(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
- graphic_hw_gl_block(ddl->dcl.con, true);
+ DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+
+ trace_dbus_update_gl(x, y, w, h);
+
glFlush();
+#ifdef CONFIG_GBM
+ graphic_hw_gl_block(ddl->dcl.con, true);
qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
x, y, w, h,
G_DBUS_CALL_FLAGS_NONE,
DBUS_DEFAULT_TIMEOUT, NULL,
dbus_update_gl_cb,
g_object_ref(ddl));
-}
-
-static void dbus_scanout_disable(DisplayChangeListener *dcl)
-{
- DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+#endif
- ddl->ds = NULL;
- qemu_dbus_display1_listener_call_disable(
- ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+#ifdef WIN32
+ switch (ddl->ds_share) {
+ case SHARE_KIND_MAPPED:
+ egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h);
+ dbus_gfx_update(dcl, x, y, w, h);
+ break;
+ case SHARE_KIND_D3DTEX:
+ Error *err = NULL;
+ assert(ddl->d3d_texture);
+
+ graphic_hw_gl_block(ddl->dcl.con, true);
+ if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) {
+ error_report_err(err);
+ return;
+ }
+ qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d(
+ ddl->d3d11_proxy,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT, NULL,
+ dbus_update_gl_cb,
+ g_object_ref(ddl));
+ break;
+ default:
+ g_warn_if_reached();
+ }
+#endif
}
+#ifdef CONFIG_GBM
static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
fd_list,
NULL, NULL, NULL);
}
+#endif /* GBM */
+#endif /* OPENGL */
+
+#ifdef WIN32
+static bool dbus_scanout_map(DBusDisplayListener *ddl)
+{
+ g_autoptr(GError) err = NULL;
+ BOOL success;
+ HANDLE target_handle;
+
+ if (ddl->ds_share == SHARE_KIND_MAPPED) {
+ return true;
+ }
+
+ if (!ddl->can_share_map || !ddl->ds->handle) {
+ return false;
+ }
+
+ success = DuplicateHandle(
+ GetCurrentProcess(),
+ ddl->ds->handle,
+ ddl->peer_process,
+ &target_handle,
+ FILE_MAP_READ | SECTION_QUERY,
+ FALSE, 0);
+ if (!success) {
+ g_autofree char *msg = g_win32_error_message(GetLastError());
+ g_debug("Failed to DuplicateHandle: %s", msg);
+ ddl->can_share_map = false;
+ return false;
+ }
+
+ if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync(
+ ddl->map_proxy,
+ GPOINTER_TO_UINT(target_handle),
+ ddl->ds->handle_offset,
+ surface_width(ddl->ds),
+ surface_height(ddl->ds),
+ surface_stride(ddl->ds),
+ surface_format(ddl->ds),
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT,
+ NULL,
+ &err)) {
+ g_debug("Failed to call ScanoutMap: %s", err->message);
+ ddl->can_share_map = false;
+ return false;
+ }
+
+ ddl->ds_share = SHARE_KIND_MAPPED;
+
+ return true;
+}
+
+static bool
+dbus_scanout_share_d3d_texture(
+ DBusDisplayListener *ddl,
+ ID3D11Texture2D *tex,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ Error *err = NULL;
+ BOOL success;
+ HANDLE share_handle, target_handle;
+
+ if (!d3d_texture2d_release0(tex, &err)) {
+ error_report_err(err);
+ return false;
+ }
+
+ if (!d3d_texture2d_share(tex, &share_handle, &err)) {
+ error_report_err(err);
+ return false;
+ }
+
+ success = DuplicateHandle(
+ GetCurrentProcess(),
+ share_handle,
+ ddl->peer_process,
+ &target_handle,
+ 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!success) {
+ g_autofree char *msg = g_win32_error_message(GetLastError());
+ g_debug("Failed to DuplicateHandle: %s", msg);
+ CloseHandle(share_handle);
+ return false;
+ }
+
+ qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d(
+ ddl->d3d11_proxy,
+ GPOINTER_TO_INT(target_handle),
+ backing_width,
+ backing_height,
+ backing_y_0_top,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, NULL, NULL);
+
+ CloseHandle(share_handle);
+
+ if (!d3d_texture2d_acquire0(tex, &err)) {
+ error_report_err(err);
+ return false;
+ }
+
+ ddl->d3d_texture = tex;
+ ddl->ds_share = SHARE_KIND_D3DTEX;
+
+ return true;
+}
+#endif
+#ifdef CONFIG_OPENGL
static void dbus_scanout_texture(DisplayChangeListener *dcl,
uint32_t tex_id,
bool backing_y_0_top,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
+ trace_dbus_scanout_texture(tex_id, backing_y_0_top,
+ backing_width, backing_height, x, y, w, h);
+#ifdef CONFIG_GBM
QemuDmaBuf dmabuf = {
.width = backing_width,
.height = backing_height,
dbus_scanout_dmabuf(dcl, &dmabuf);
close(dmabuf.fd);
+#endif
+
+#ifdef WIN32
+ DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+
+ /* there must be a matching gfx_switch before */
+ assert(surface_width(ddl->ds) == w);
+ assert(surface_height(ddl->ds) == h);
+
+ if (d3d_tex2d) {
+ dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top,
+ backing_width, backing_height, x, y, w, h);
+ } else {
+ dbus_scanout_map(ddl);
+ egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false);
+ }
+#endif
}
+#ifdef CONFIG_GBM
static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf, bool have_hot,
uint32_t hot_x, uint32_t hot_y)
NULL);
}
-static void dbus_cursor_position(DisplayChangeListener *dcl,
+static void dbus_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ dbus_scanout_disable(dcl);
+}
+#endif /* GBM */
+
+static void dbus_gl_cursor_position(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y)
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
-static void dbus_release_dmabuf(DisplayChangeListener *dcl,
- QemuDmaBuf *dmabuf)
-{
- dbus_scanout_disable(dcl);
-}
-
static void dbus_scanout_update(DisplayChangeListener *dcl,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h)
{
- DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
-
- dbus_call_update_gl(ddl, x, y, w, h);
+ dbus_call_update_gl(dcl, x, y, w, h);
}
static void dbus_gl_refresh(DisplayChangeListener *dcl)
}
if (ddl->gl_updates) {
- dbus_call_update_gl(ddl, 0, 0,
+ dbus_call_update_gl(dcl, 0, 0,
surface_width(ddl->ds), surface_height(ddl->ds));
ddl->gl_updates = 0;
}
}
-#endif
+#endif /* OPENGL */
static void dbus_refresh(DisplayChangeListener *dcl)
{
graphic_hw_update(dcl->con);
}
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
size_t stride;
assert(ddl->ds);
- stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
trace_dbus_update(x, y, w, h);
+#ifdef WIN32
+ if (dbus_scanout_map(ddl)) {
+ qemu_dbus_display1_listener_win32_map_call_update_map(
+ ddl->map_proxy,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+ return;
+ }
+#endif
+
if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
v_data = g_variant_new_from_data(
G_VARIANT_TYPE("ay"),
}
/* make a copy, since gvariant only handles linear data */
+ stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
img = pixman_image_create_bits(surface_format(ddl->ds),
w, h, NULL, stride);
pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
}
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
struct DisplaySurface *new_surface)
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+ trace_dbus_gl_gfx_switch(new_surface);
+
ddl->ds = new_surface;
+ ddl->ds_share = SHARE_KIND_NONE;
if (ddl->ds) {
int width = surface_width(ddl->ds);
int height = surface_height(ddl->ds);
/* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
- width, height, 0, 0, width, height);
+ width, height, 0, 0, width, height, NULL);
}
}
#endif
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
ddl->ds = new_surface;
- if (!ddl->ds) {
- /* why not call disable instead? */
- return;
- }
+ ddl->ds_share = SHARE_KIND_NONE;
}
static void dbus_mouse_set(DisplayChangeListener *dcl,
NULL);
}
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
const DisplayChangeListenerOps dbus_gl_dcl_ops = {
.dpy_name = "dbus-gl",
.dpy_gfx_update = dbus_gl_gfx_update,
.dpy_gl_scanout_disable = dbus_scanout_disable,
.dpy_gl_scanout_texture = dbus_scanout_texture,
+#ifdef CONFIG_GBM
.dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf,
.dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf,
- .dpy_gl_cursor_position = dbus_cursor_position,
.dpy_gl_release_dmabuf = dbus_release_dmabuf,
+#endif
+ .dpy_gl_cursor_position = dbus_gl_cursor_position,
.dpy_gl_update = dbus_scanout_update,
};
#endif
g_clear_object(&ddl->conn);
g_clear_pointer(&ddl->bus_name, g_free);
g_clear_object(&ddl->proxy);
+#ifdef WIN32
+ g_clear_object(&ddl->map_proxy);
+ g_clear_object(&ddl->d3d11_proxy);
+ g_clear_pointer(&ddl->peer_process, CloseHandle);
+#ifdef CONFIG_OPENGL
+ egl_fb_destroy(&ddl->fb);
+#endif
+#endif
G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
}
DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
ddl->dcl.ops = &dbus_dcl_ops;
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
if (display_opengl) {
ddl->dcl.ops = &dbus_gl_dcl_ops;
}
return ddl->console;
}
+#ifdef WIN32
+static bool
+dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
+{
+ QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
+ bool implements;
+
+ implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
+ if (!implements) {
+ g_debug("Display listener does not implement: `%s`", iface);
+ }
+
+ return implements;
+}
+
+static bool
+dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
+{
+ g_autoptr(GError) err = NULL;
+ GDBusConnection *conn;
+ GIOStream *stream;
+ GSocket *sock;
+ g_autoptr(GCredentials) creds = NULL;
+ DWORD *pid;
+
+ if (ddl->peer_process) {
+ return true;
+ }
+
+ conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy));
+ stream = g_dbus_connection_get_stream(conn);
+
+ if (!G_IS_UNIX_CONNECTION(stream)) {
+ return false;
+ }
+
+ sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
+ creds = g_socket_get_credentials(sock, &err);
+
+ if (!creds) {
+ g_debug("Failed to get peer credentials: %s", err->message);
+ return false;
+ }
+
+ pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
+
+ if (pid == NULL) {
+ g_debug("Failed to get peer PID");
+ return false;
+ }
+
+ ddl->peer_process = OpenProcess(
+ PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
+ false, *pid);
+
+ if (!ddl->peer_process) {
+ g_autofree char *msg = g_win32_error_message(GetLastError());
+ g_debug("Failed to OpenProcess: %s", msg);
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+static void
+dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl)
+{
+#ifdef WIN32
+ g_autoptr(GError) err = NULL;
+
+ if (!dbus_display_listener_implements(ddl,
+ "org.qemu.Display1.Listener.Win32.D3d11")) {
+ return;
+ }
+
+ if (!dbus_display_listener_setup_peer_process(ddl)) {
+ return;
+ }
+
+ ddl->d3d11_proxy =
+ qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "/org/qemu/Display1/Listener",
+ NULL,
+ &err);
+ if (!ddl->d3d11_proxy) {
+ g_debug("Failed to setup win32 d3d11 proxy: %s", err->message);
+ return;
+ }
+#endif
+}
+
+static void
+dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
+{
+#ifdef WIN32
+ g_autoptr(GError) err = NULL;
+
+ if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) {
+ return;
+ }
+
+ if (!dbus_display_listener_setup_peer_process(ddl)) {
+ return;
+ }
+
+ ddl->map_proxy =
+ qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "/org/qemu/Display1/Listener",
+ NULL,
+ &err);
+ if (!ddl->map_proxy) {
+ g_debug("Failed to setup win32 map proxy: %s", err->message);
+ return;
+ }
+
+ ddl->can_share_map = true;
+#endif
+}
+
DBusDisplayListener *
dbus_display_listener_new(const char *bus_name,
GDBusConnection *conn,
ddl->conn = conn;
ddl->console = console;
+ dbus_display_listener_setup_shared_map(ddl);
+ dbus_display_listener_setup_d3d11(ddl);
+
con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
assert(con);
ddl->dcl.con = con;
static QEMUGLContext dbus_create_context(DisplayGLCtx *dgc,
QEMUGLParams *params)
{
-#ifdef CONFIG_GBM
eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
qemu_egl_rn_ctx);
-#endif
return qemu_egl_create_context(dgc, params);
}
DisplayChangeListener *dcl)
{
return
-#ifdef CONFIG_GBM
dcl->ops == &dbus_gl_dcl_ops ||
-#endif
dcl->ops == &dbus_console_dcl_ops;
}
Notifier notifier;
};
+#ifdef WIN32
+bool
+dbus_win32_import_socket(GDBusMethodInvocation *invocation,
+ GVariant *arg_listener, int *socket);
+#endif
+
#define TYPE_DBUS_DISPLAY "dbus-display"
OBJECT_DECLARE_SIMPLE_TYPE(DBusDisplay, DBUS_DISPLAY)
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "ui/egl-context.h"
QEMUGLContext qemu_egl_create_context(DisplayGLCtx *dgc,
int qemu_egl_make_context_current(DisplayGLCtx *dgc,
QEMUGLContext ctx)
{
- return eglMakeCurrent(qemu_egl_display,
- EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
+ if (!eglMakeCurrent(qemu_egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, ctx)) {
+ error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string());
+ return -1;
+ }
+
+ return 0;
}
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
}
}
+#ifdef CONFIG_GBM
+
static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height);
+ 0, 0, dmabuf->width, dmabuf->height, NULL);
}
static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
}
}
+static void egl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ egl_dmabuf_release_texture(dmabuf);
+}
+
+#endif
+
static void egl_cursor_position(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y)
{
edpy->pos_y = pos_y;
}
-static void egl_release_dmabuf(DisplayChangeListener *dcl,
- QemuDmaBuf *dmabuf)
-{
- egl_dmabuf_release_texture(dmabuf);
-}
-
static void egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h)
.dpy_gl_scanout_disable = egl_scanout_disable,
.dpy_gl_scanout_texture = egl_scanout_texture,
+#ifdef CONFIG_GBM
.dpy_gl_scanout_dmabuf = egl_scanout_dmabuf,
.dpy_gl_cursor_dmabuf = egl_cursor_dmabuf,
- .dpy_gl_cursor_position = egl_cursor_position,
.dpy_gl_release_dmabuf = egl_release_dmabuf,
+#endif
+ .dpy_gl_cursor_position = egl_cursor_position,
.dpy_gl_update = egl_scanout_flush,
};
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
+
#include "qemu/drm.h"
#include "qemu/error-report.h"
#include "ui/console.h"
#include "ui/egl-helpers.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
+#include "trace.h"
EGLDisplay *qemu_egl_display;
EGLConfig qemu_egl_config;
DisplayGLMode qemu_egl_mode;
+bool qemu_egl_angle_d3d;
/* ------------------------------------------------------------------ */
-#if defined(CONFIG_X11) || defined(CONFIG_GBM)
-static const char *egl_get_error_string(void)
+const char *qemu_egl_get_error_string(void)
{
EGLint error = eglGetError();
return "Unknown EGL error";
}
}
-#endif
static void egl_fb_delete_texture(egl_fb *fb)
{
GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst));
}
+void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h)
+{
+ assert(surface_width(dst) == src->width);
+ assert(surface_height(dst) == src->height);
+ assert(surface_format(dst) == PIXMAN_x8r8g8b8);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ glPixelStorei(GL_PACK_ROW_LENGTH, surface_stride(dst) / 4);
+ glReadPixels(x, y, w, h,
+ GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst) + x * 4);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+}
+
void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip)
{
glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
/* ---------------------------------------------------------------------- */
+EGLContext qemu_egl_rn_ctx;
+
#ifdef CONFIG_GBM
int qemu_egl_rn_fd;
struct gbm_device *qemu_egl_rn_gbm_dev;
-EGLContext qemu_egl_rn_ctx;
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
{
/* ---------------------------------------------------------------------- */
-#if defined(CONFIG_X11) || defined(CONFIG_GBM)
+#if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(WIN32)
/*
* Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed
/* In practise any EGL 1.5 implementation would support the EXT extension */
if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
- PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
- (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
- if (getPlatformDisplayEXT && platform != 0) {
- dpy = getPlatformDisplayEXT(platform, native, NULL);
+ if (platform != 0) {
+ dpy = eglGetPlatformDisplayEXT(platform, native, NULL);
}
}
qemu_egl_display = qemu_egl_get_display(dpy, platform);
if (qemu_egl_display == EGL_NO_DISPLAY) {
- error_report("egl: eglGetDisplay failed: %s", egl_get_error_string());
+ error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string());
return -1;
}
b = eglInitialize(qemu_egl_display, &major, &minor);
if (b == EGL_FALSE) {
- error_report("egl: eglInitialize failed: %s", egl_get_error_string());
+ error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string());
return -1;
}
b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
if (b == EGL_FALSE) {
error_report("egl: eglBindAPI failed (%s mode): %s",
- gles ? "gles" : "core", egl_get_error_string());
+ gles ? "gles" : "core", qemu_egl_get_error_string());
return -1;
}
&qemu_egl_config, 1, &n);
if (b == EGL_FALSE || n != 1) {
error_report("egl: eglChooseConfig failed (%s mode): %s",
- gles ? "gles" : "core", egl_get_error_string());
+ gles ? "gles" : "core", qemu_egl_get_error_string());
return -1;
}
return 0;
}
+#endif
+
+#if defined(CONFIG_X11) || defined(CONFIG_GBM)
int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
{
#ifdef EGL_KHR_platform_x11
return qemu_egl_init_dpy(dpy, 0, mode);
#endif
}
+#endif
+
+
+#ifdef WIN32
+int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode)
+{
+ /* prefer GL ES, as that's what ANGLE supports */
+ if (mode == DISPLAYGL_MODE_ON) {
+ mode = DISPLAYGL_MODE_ES;
+ }
+
+ if (qemu_egl_init_dpy(dpy, 0, mode) < 0) {
+ return -1;
+ }
+
+#ifdef EGL_D3D11_DEVICE_ANGLE
+ if (epoxy_has_egl_extension(qemu_egl_display, "EGL_EXT_device_query")) {
+ EGLDeviceEXT device;
+ void *d3d11_device;
+ if (!eglQueryDisplayAttribEXT(qemu_egl_display,
+ EGL_DEVICE_EXT,
+ (EGLAttrib *)&device)) {
+ return 0;
+ }
+
+ if (!eglQueryDeviceAttribEXT(device,
+ EGL_D3D11_DEVICE_ANGLE,
+ (EGLAttrib *)&d3d11_device)) {
+ return 0;
+ }
+
+ trace_egl_init_d3d11_device(device);
+ qemu_egl_angle_d3d = device != NULL;
+ }
+#endif
+
+ return 0;
+}
#endif
bool qemu_egl_has_dmabuf(void)
return false;
}
-#ifdef CONFIG_GBM
+#ifdef WIN32
+ if (qemu_egl_init_dpy_win32(EGL_DEFAULT_DISPLAY, mode) < 0) {
+ error_setg(errp, "egl: init failed");
+ return false;
+ }
+ qemu_egl_rn_ctx = qemu_egl_init_ctx();
+ if (!qemu_egl_rn_ctx) {
+ error_setg(errp, "egl: egl_init_ctx failed");
+ return false;
+ }
+#elif defined(CONFIG_GBM)
if (egl_rendernode_init(rendernode, mode) < 0) {
error_setg(errp, "egl: render node init failed");
return false;
}
+#endif
+
+ if (!qemu_egl_rn_ctx) {
+ error_setg(errp, "egl: not available on this platform");
+ return false;
+ }
+
display_opengl = 1;
return true;
-#else
- error_setg(errp, "egl: not available on this platform");
- return false;
-#endif
}
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
#include "trace.h"
uint32_t backing_id, bool backing_y_0_top,
uint32_t backing_width, uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
gd_egl_scanout_texture(dcl, dmabuf->texture,
dmabuf->y0_top, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height);
+ dmabuf->x, dmabuf->y, dmabuf->scanout_width,
+ dmabuf->scanout_height, NULL);
if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf;
{
VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
- return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
- vc->gfx.esurface, ctx);
+ if (!eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, ctx)) {
+ error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string());
+ return -1;
+ }
+
+ return 0;
}
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
gd_gl_area_scanout_texture(dcl, dmabuf->texture,
dmabuf->y0_top, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height);
+ dmabuf->x, dmabuf->y, dmabuf->scanout_width,
+ dmabuf->scanout_height, NULL);
if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf;
DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
TYPE_CHARDEV_VC)
-struct touch_slot {
- int x;
- int y;
- int tracking_id;
-};
static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX];
bool gtk_use_gl_area;
QemuDmaBuf *dmabuf)
{
#ifdef CONFIG_GBM
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
egl_dmabuf_release_texture(dmabuf);
+ if (vc->gfx.guest_fb.dmabuf == dmabuf) {
+ vc->gfx.guest_fb.dmabuf = NULL;
+ }
#endif
}
void *opaque)
{
VirtualConsole *vc = opaque;
- struct touch_slot *slot;
uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence);
- bool needs_sync = false;
- int update;
int type = -1;
- int i;
-
- if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
- warn_report("gtk: unexpected touch slot number: % " PRId64" >= %d\n",
- num_slot, INPUT_EVENT_SLOTS_MAX);
- return FALSE;
- }
-
- slot = &touch_slots[num_slot];
- slot->x = touch->x;
- slot->y = touch->y;
switch (touch->type) {
case GDK_TOUCH_BEGIN:
type = INPUT_MULTI_TOUCH_TYPE_BEGIN;
- slot->tracking_id = num_slot;
break;
case GDK_TOUCH_UPDATE:
type = INPUT_MULTI_TOUCH_TYPE_UPDATE;
break;
default:
warn_report("gtk: unexpected touch event type\n");
+ return FALSE;
}
- for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
- if (i == num_slot) {
- update = type;
- } else {
- update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
- }
-
- slot = &touch_slots[i];
-
- if (slot->tracking_id == -1) {
- continue;
- }
-
- if (update == INPUT_MULTI_TOUCH_TYPE_END) {
- slot->tracking_id = -1;
- qemu_input_queue_mtt(vc->gfx.dcl.con, update, i, slot->tracking_id);
- needs_sync = true;
- } else {
- qemu_input_queue_mtt(vc->gfx.dcl.con, update, i, slot->tracking_id);
- qemu_input_queue_btn(vc->gfx.dcl.con, INPUT_BUTTON_TOUCH, true);
- qemu_input_queue_mtt_abs(vc->gfx.dcl.con,
- INPUT_AXIS_X, (int) slot->x,
- 0, surface_width(vc->gfx.ds),
- i, slot->tracking_id);
- qemu_input_queue_mtt_abs(vc->gfx.dcl.con,
- INPUT_AXIS_Y, (int) slot->y,
- 0, surface_height(vc->gfx.ds),
- i, slot->tracking_id);
- needs_sync = true;
- }
- }
-
- if (needs_sync) {
- qemu_input_event_sync();
- }
-
+ console_handle_touch_event(vc->gfx.dcl.con, touch_slots,
+ num_slot, surface_width(vc->gfx.ds),
+ surface_height(vc->gfx.ds), touch->x,
+ touch->y, type, &error_warn);
return TRUE;
}
ui_modules += {'opengl' : opengl_ss}
endif
-if opengl.found() and gbm.found()
+if opengl.found()
egl_headless_ss = ss.source_set()
- egl_headless_ss.add(when: [opengl, gbm, pixman],
- if_true: files('egl-headless.c'))
+ egl_headless_ss.add(when: [opengl, pixman],
+ if_true: [files('egl-headless.c'), gbm])
ui_modules += {'egl-headless' : egl_headless_ss}
endif
if dbus_display
dbus_ss = ss.source_set()
+ env = environment()
+ env.set('TARGETOS', targetos)
+ xml = custom_target('dbus-display preprocess',
+ input: 'dbus-display1.xml',
+ output: 'dbus-display1.xml',
+ env: env,
+ command: [xml_pp, '@INPUT@', '@OUTPUT@'])
dbus_display1 = custom_target('dbus-display gdbus-codegen',
output: ['dbus-display1.h', 'dbus-display1.c'],
- input: files('dbus-display1.xml'),
+ input: xml,
command: [gdbus_codegen, '@INPUT@',
'--glib-min-required', '2.64',
'--output-directory', meson.current_build_dir(),
#include "qemu/osdep.h"
#include "ui/console.h"
#include "standard-headers/drm/drm_fourcc.h"
+#include "trace.h"
PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format)
{
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver);
SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
- }
- scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
- if (scon->opengl) {
scon->winctx = SDL_GL_CreateContext(scon->real_window);
+ } else {
+ /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */
+ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
}
sdl_update_caption(scon);
}
return;
}
- SDL_GL_DeleteContext(scon->winctx);
- scon->winctx = NULL;
- SDL_DestroyRenderer(scon->real_renderer);
- scon->real_renderer = NULL;
+ if (scon->winctx) {
+ SDL_GL_DeleteContext(scon->winctx);
+ scon->winctx = NULL;
+ }
+ if (scon->real_renderer) {
+ SDL_DestroyRenderer(scon->real_renderer);
+ scon->real_renderer = NULL;
+ }
SDL_DestroyWindow(scon->real_window);
scon->real_window = NULL;
}
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
EGLint stride = 0, fourcc = 0;
console_txt_new(int w, int h) "%dx%d"
console_select(int nr) "%d"
console_refresh(int interval) "interval %d ms"
-displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d"
+displaysurface_create(int w, int h) "%dx%d"
displaysurface_create_from(void *display_surface, int w, int h, uint32_t format) "surface=%p, %dx%d, format 0x%x"
displaysurface_create_pixman(void *display_surface) "surface=%p"
displaysurface_free(void *display_surface) "surface=%p"
dbus_mouse_release(unsigned int button) "button %u"
dbus_mouse_set_pos(unsigned int x, unsigned int y) "x=%u, y=%u"
dbus_mouse_rel_motion(int dx, int dy) "dx=%d, dy=%d"
+dbus_touch_send_event(unsigned int kind, uint32_t num_slot, uint32_t x, uint32_t y) "kind=%u, num_slot=%u, x=%d, y=%d"
dbus_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
+dbus_update_gl(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
dbus_clipboard_grab_failed(void) ""
dbus_clipboard_register(const char *bus_name) "peer %s"
dbus_clipboard_unregister(const char *bus_name) "peer %s"
+dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u"
+dbus_gl_gfx_switch(void *p) "surf: %p"
+
+# egl-helpers.c
+egl_init_d3d11_device(void *p) "d3d device: %p"
*/
return qemu_fdatasync(fd);
}
+
+void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp)
+{
+ void *bits;
+
+ trace_win32_map_alloc(size);
+
+ *h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+ size, NULL);
+ if (*h == NULL) {
+ error_setg_win32(errp, GetLastError(), "Failed to CreateFileMapping");
+ return NULL;
+ }
+
+ bits = MapViewOfFile(*h, FILE_MAP_ALL_ACCESS, 0, 0, size);
+ if (bits == NULL) {
+ error_setg_win32(errp, GetLastError(), "Failed to MapViewOfFile");
+ CloseHandle(*h);
+ return NULL;
+ }
+
+ return bits;
+}
+
+void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp)
+{
+ trace_win32_map_free(ptr, h);
+
+ if (UnmapViewOfFile(ptr) == 0) {
+ error_setg_win32(errp, GetLastError(), "Failed to UnmapViewOfFile");
+ }
+ CloseHandle(h);
+}
qemu_vfree(void *ptr) "ptr %p"
qemu_anon_ram_free(void *ptr, size_t size) "ptr %p size %zu"
+# oslib-win32.c
+win32_map_alloc(size_t size) "size:%zd"
+win32_map_free(void *ptr, void *h) "ptr:%p handle:%p"
+
# hbitmap.c
hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx"
hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64