2012-04-09 18:50:52 +02:00
|
|
|
/*
|
|
|
|
* Common CPU TLB handling
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2019-01-23 15:08:56 +01:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2012-04-09 18:50:52 +02:00
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2016-01-26 19:16:56 +01:00
|
|
|
#include "qemu/osdep.h"
|
tcg: drop global lock during TCG code execution
This finally allows TCG to benefit from the iothread introduction: Drop
the global mutex while running pure TCG CPU code. Reacquire the lock
when entering MMIO or PIO emulation, or when leaving the TCG loop.
We have to revert a few optimization for the current TCG threading
model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
kicking it in qemu_cpu_kick. We also need to disable RAM block
reordering until we have a more efficient locking mechanism at hand.
Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
These numbers demonstrate where we gain something:
20338 jan 20 0 331m 75m 6904 R 99 0.9 0:50.95 qemu-system-arm
20337 jan 20 0 331m 75m 6904 S 20 0.9 0:26.50 qemu-system-arm
The guest CPU was fully loaded, but the iothread could still run mostly
independent on a second core. Without the patch we don't get beyond
32206 jan 20 0 330m 73m 7036 R 82 0.9 1:06.00 qemu-system-arm
32204 jan 20 0 330m 73m 7036 S 21 0.9 0:17.03 qemu-system-arm
We don't benefit significantly, though, when the guest is not fully
loading a host CPU.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
[FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
[EGC: fixed iothread lock for cpu-exec IRQ handling]
Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
[PM: target-arm changes]
Acked-by: Peter Maydell <peter.maydell@linaro.org>
2017-02-23 19:29:11 +01:00
|
|
|
#include "qemu/main-loop.h"
|
2021-02-04 17:39:23 +01:00
|
|
|
#include "hw/core/tcg-cpu-ops.h"
|
2012-12-17 18:19:49 +01:00
|
|
|
#include "exec/exec-all.h"
|
|
|
|
#include "exec/memory.h"
|
2014-03-28 19:42:10 +01:00
|
|
|
#include "exec/cpu_ldst.h"
|
2012-12-17 18:19:49 +01:00
|
|
|
#include "exec/cputlb.h"
|
|
|
|
#include "exec/memory-internal.h"
|
2013-10-14 17:13:59 +02:00
|
|
|
#include "exec/ram_addr.h"
|
2014-03-28 17:55:24 +01:00
|
|
|
#include "tcg/tcg.h"
|
cputlb: don't cpu_abort() if guest tries to execute outside RAM or RAM
In get_page_addr_code(), if the guest program counter turns out not to
be in ROM or RAM, we can't handle executing from it, and we call
cpu_abort(). This results in the message
qemu: fatal: Trying to execute code outside RAM or ROM at 0x08000000
followed by a guest register dump, and then QEMU dumps core.
This situation happens in one of two cases:
(1) a guest kernel bug, where it jumped off into nowhere
(2) a user command line mistake, where they tried to run an image for
board A on a QEMU model of board B, or where they didn't provide
an image at all, and QEMU executed through a ROM or RAM full of
NOP instructions and then fell off the end
In either case, a core dump of QEMU itself is entirely useless, and
only confuses users into thinking that this is a bug in QEMU rather
than a bug in the guest or a problem with their command line. (This
is a variation on the general idea that we shouldn't assert() on
something the user can accidentally provoke.)
Replace the cpu_abort() with something that explains the situation
a bit better and exits QEMU without dumping core.
(See LP:1062220 for several examples of confused users.)
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Message-id: 1466442425-11885-1-git-send-email-peter.maydell@linaro.org
2016-06-20 19:07:05 +02:00
|
|
|
#include "qemu/error-report.h"
|
|
|
|
#include "exec/log.h"
|
2023-04-01 05:13:36 +02:00
|
|
|
#include "exec/helper-proto-common.h"
|
2016-06-28 20:37:27 +02:00
|
|
|
#include "qemu/atomic.h"
|
2018-08-16 01:31:47 +02:00
|
|
|
#include "qemu/atomic128.h"
|
2020-12-16 13:27:58 +01:00
|
|
|
#include "exec/translate-all.h"
|
2022-12-09 10:36:46 +01:00
|
|
|
#include "trace.h"
|
2021-05-24 19:04:53 +02:00
|
|
|
#include "tb-hash.h"
|
2021-01-17 17:48:12 +01:00
|
|
|
#include "internal.h"
|
2019-06-19 21:20:08 +02:00
|
|
|
#ifdef CONFIG_PLUGIN
|
|
|
|
#include "qemu/plugin-memory.h"
|
|
|
|
#endif
|
2021-07-27 23:10:22 +02:00
|
|
|
#include "tcg/tcg-ldst.h"
|
2023-03-28 03:32:36 +02:00
|
|
|
#include "tcg/oversized-guest.h"
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2016-03-15 15:30:24 +01:00
|
|
|
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
|
|
|
|
/* #define DEBUG_TLB */
|
|
|
|
/* #define DEBUG_TLB_LOG */
|
|
|
|
|
|
|
|
#ifdef DEBUG_TLB
|
|
|
|
# define DEBUG_TLB_GATE 1
|
|
|
|
# ifdef DEBUG_TLB_LOG
|
|
|
|
# define DEBUG_TLB_LOG_GATE 1
|
|
|
|
# else
|
|
|
|
# define DEBUG_TLB_LOG_GATE 0
|
|
|
|
# endif
|
|
|
|
#else
|
|
|
|
# define DEBUG_TLB_GATE 0
|
|
|
|
# define DEBUG_TLB_LOG_GATE 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define tlb_debug(fmt, ...) do { \
|
|
|
|
if (DEBUG_TLB_LOG_GATE) { \
|
|
|
|
qemu_log_mask(CPU_LOG_MMU, "%s: " fmt, __func__, \
|
|
|
|
## __VA_ARGS__); \
|
|
|
|
} else if (DEBUG_TLB_GATE) { \
|
|
|
|
fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2018-10-09 19:45:55 +02:00
|
|
|
#define assert_cpu_is_self(cpu) do { \
|
2017-02-23 19:29:16 +01:00
|
|
|
if (DEBUG_TLB_GATE) { \
|
2018-10-09 19:45:55 +02:00
|
|
|
g_assert(!(cpu)->created || qemu_cpu_is_self(cpu)); \
|
2017-02-23 19:29:16 +01:00
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2017-02-23 19:29:18 +01:00
|
|
|
/* run_on_cpu_data.target_ptr should always be big enough for a
|
|
|
|
* target_ulong even on 32 bit builds */
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data));
|
|
|
|
|
2017-02-23 19:29:20 +01:00
|
|
|
/* We currently can't handle more than 16 bits in the MMUIDX bitmask.
|
|
|
|
*/
|
|
|
|
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
|
|
|
|
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
|
|
|
|
2019-12-07 20:47:41 +01:00
|
|
|
static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
|
2019-12-07 20:37:57 +01:00
|
|
|
{
|
2019-12-07 20:47:41 +01:00
|
|
|
return (fast->mask >> CPU_TLB_ENTRY_BITS) + 1;
|
2019-12-07 20:37:57 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 20:47:41 +01:00
|
|
|
static inline size_t sizeof_tlb(CPUTLBDescFast *fast)
|
2019-01-16 18:01:13 +01:00
|
|
|
{
|
2019-12-07 20:47:41 +01:00
|
|
|
return fast->mask + (1 << CPU_TLB_ENTRY_BITS);
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
|
2019-03-22 16:36:40 +01:00
|
|
|
static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
|
2019-01-16 18:01:13 +01:00
|
|
|
size_t max_entries)
|
|
|
|
{
|
2019-03-22 16:36:40 +01:00
|
|
|
desc->window_begin_ns = ns;
|
|
|
|
desc->window_max_entries = max_entries;
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:28 +02:00
|
|
|
static void tb_jmp_cache_clear_page(CPUState *cpu, vaddr page_addr)
|
2021-01-21 06:53:20 +01:00
|
|
|
{
|
2022-08-15 22:13:05 +02:00
|
|
|
CPUJumpCache *jc = cpu->tb_jmp_cache;
|
2023-02-03 18:15:10 +01:00
|
|
|
int i, i0;
|
2021-01-21 06:53:20 +01:00
|
|
|
|
2023-02-03 18:15:10 +01:00
|
|
|
if (unlikely(!jc)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
i0 = tb_jmp_cache_hash_page(page_addr);
|
2021-01-21 06:53:20 +01:00
|
|
|
for (i = 0; i < TB_JMP_PAGE_SIZE; i++) {
|
2022-08-15 22:13:05 +02:00
|
|
|
qatomic_set(&jc->array[i0 + i].tb, NULL);
|
2021-01-21 06:53:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-16 18:01:13 +01:00
|
|
|
/**
|
|
|
|
* tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary
|
2019-12-07 20:58:50 +01:00
|
|
|
* @desc: The CPUTLBDesc portion of the TLB
|
|
|
|
* @fast: The CPUTLBDescFast portion of the same TLB
|
2019-01-16 18:01:13 +01:00
|
|
|
*
|
|
|
|
* Called with tlb_lock_held.
|
|
|
|
*
|
|
|
|
* We have two main constraints when resizing a TLB: (1) we only resize it
|
|
|
|
* on a TLB flush (otherwise we'd have to take a perf hit by either rehashing
|
|
|
|
* the array or unnecessarily flushing it), which means we do not control how
|
|
|
|
* frequently the resizing can occur; (2) we don't have access to the guest's
|
|
|
|
* future scheduling decisions, and therefore have to decide the magnitude of
|
|
|
|
* the resize based on past observations.
|
|
|
|
*
|
|
|
|
* In general, a memory-hungry process can benefit greatly from an appropriately
|
|
|
|
* sized TLB, since a guest TLB miss is very expensive. This doesn't mean that
|
|
|
|
* we just have to make the TLB as large as possible; while an oversized TLB
|
|
|
|
* results in minimal TLB miss rates, it also takes longer to be flushed
|
|
|
|
* (flushes can be _very_ frequent), and the reduced locality can also hurt
|
|
|
|
* performance.
|
|
|
|
*
|
|
|
|
* To achieve near-optimal performance for all kinds of workloads, we:
|
|
|
|
*
|
|
|
|
* 1. Aggressively increase the size of the TLB when the use rate of the
|
|
|
|
* TLB being flushed is high, since it is likely that in the near future this
|
|
|
|
* memory-hungry process will execute again, and its memory hungriness will
|
|
|
|
* probably be similar.
|
|
|
|
*
|
|
|
|
* 2. Slowly reduce the size of the TLB as the use rate declines over a
|
|
|
|
* reasonably large time window. The rationale is that if in such a time window
|
|
|
|
* we have not observed a high TLB use rate, it is likely that we won't observe
|
|
|
|
* it in the near future. In that case, once a time window expires we downsize
|
|
|
|
* the TLB to match the maximum use rate observed in the window.
|
|
|
|
*
|
|
|
|
* 3. Try to keep the maximum use rate in a time window in the 30-70% range,
|
|
|
|
* since in that range performance is likely near-optimal. Recall that the TLB
|
|
|
|
* is direct mapped, so we want the use rate to be low (or at least not too
|
|
|
|
* high), since otherwise we are likely to have a significant amount of
|
|
|
|
* conflict misses.
|
|
|
|
*/
|
2019-12-07 23:36:01 +01:00
|
|
|
static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
|
|
|
|
int64_t now)
|
2019-01-16 18:01:13 +01:00
|
|
|
{
|
2019-12-07 20:58:50 +01:00
|
|
|
size_t old_size = tlb_n_entries(fast);
|
2019-01-16 18:01:13 +01:00
|
|
|
size_t rate;
|
|
|
|
size_t new_size = old_size;
|
|
|
|
int64_t window_len_ms = 100;
|
|
|
|
int64_t window_len_ns = window_len_ms * 1000 * 1000;
|
2019-03-22 16:36:40 +01:00
|
|
|
bool window_expired = now > desc->window_begin_ns + window_len_ns;
|
2019-01-16 18:01:13 +01:00
|
|
|
|
2019-03-22 16:36:40 +01:00
|
|
|
if (desc->n_used_entries > desc->window_max_entries) {
|
|
|
|
desc->window_max_entries = desc->n_used_entries;
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
2019-03-22 16:36:40 +01:00
|
|
|
rate = desc->window_max_entries * 100 / old_size;
|
2019-01-16 18:01:13 +01:00
|
|
|
|
|
|
|
if (rate > 70) {
|
|
|
|
new_size = MIN(old_size << 1, 1 << CPU_TLB_DYN_MAX_BITS);
|
|
|
|
} else if (rate < 30 && window_expired) {
|
2019-03-22 16:36:40 +01:00
|
|
|
size_t ceil = pow2ceil(desc->window_max_entries);
|
|
|
|
size_t expected_rate = desc->window_max_entries * 100 / ceil;
|
2019-01-16 18:01:13 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Avoid undersizing when the max number of entries seen is just below
|
|
|
|
* a pow2. For instance, if max_entries == 1025, the expected use rate
|
|
|
|
* would be 1025/2048==50%. However, if max_entries == 1023, we'd get
|
|
|
|
* 1023/1024==99.9% use rate, so we'd likely end up doubling the size
|
|
|
|
* later. Thus, make sure that the expected use rate remains below 70%.
|
|
|
|
* (and since we double the size, that means the lowest rate we'd
|
|
|
|
* expect to get is 35%, which is still in the 30-70% range where
|
|
|
|
* we consider that the size is appropriate.)
|
|
|
|
*/
|
|
|
|
if (expected_rate > 70) {
|
|
|
|
ceil *= 2;
|
|
|
|
}
|
|
|
|
new_size = MAX(ceil, 1 << CPU_TLB_DYN_MIN_BITS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_size == old_size) {
|
|
|
|
if (window_expired) {
|
2019-03-22 16:36:40 +01:00
|
|
|
tlb_window_reset(desc, now, desc->n_used_entries);
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-07 20:58:50 +01:00
|
|
|
g_free(fast->table);
|
2022-08-19 23:20:37 +02:00
|
|
|
g_free(desc->fulltlb);
|
2019-01-16 18:01:13 +01:00
|
|
|
|
2019-03-22 16:36:40 +01:00
|
|
|
tlb_window_reset(desc, now, 0);
|
2019-01-16 18:01:13 +01:00
|
|
|
/* desc->n_used_entries is cleared by the caller */
|
2019-12-07 20:58:50 +01:00
|
|
|
fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
|
|
|
|
fast->table = g_try_new(CPUTLBEntry, new_size);
|
2022-08-19 23:20:37 +02:00
|
|
|
desc->fulltlb = g_try_new(CPUTLBEntryFull, new_size);
|
2019-12-07 20:58:50 +01:00
|
|
|
|
2019-01-16 18:01:13 +01:00
|
|
|
/*
|
|
|
|
* If the allocations fail, try smaller sizes. We just freed some
|
|
|
|
* memory, so going back to half of new_size has a good chance of working.
|
|
|
|
* Increased memory pressure elsewhere in the system might cause the
|
|
|
|
* allocations to fail though, so we progressively reduce the allocation
|
|
|
|
* size, aborting if we cannot even allocate the smallest TLB we support.
|
|
|
|
*/
|
2022-08-19 23:20:37 +02:00
|
|
|
while (fast->table == NULL || desc->fulltlb == NULL) {
|
2019-01-16 18:01:13 +01:00
|
|
|
if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) {
|
|
|
|
error_report("%s: %s", __func__, strerror(errno));
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
new_size = MAX(new_size >> 1, 1 << CPU_TLB_DYN_MIN_BITS);
|
2019-12-07 20:58:50 +01:00
|
|
|
fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
|
2019-01-16 18:01:13 +01:00
|
|
|
|
2019-12-07 20:58:50 +01:00
|
|
|
g_free(fast->table);
|
2022-08-19 23:20:37 +02:00
|
|
|
g_free(desc->fulltlb);
|
2019-12-07 20:58:50 +01:00
|
|
|
fast->table = g_try_new(CPUTLBEntry, new_size);
|
2022-08-19 23:20:37 +02:00
|
|
|
desc->fulltlb = g_try_new(CPUTLBEntryFull, new_size);
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-07 21:08:04 +01:00
|
|
|
static void tlb_mmu_flush_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast)
|
2019-01-16 18:01:13 +01:00
|
|
|
{
|
2019-12-07 21:00:56 +01:00
|
|
|
desc->n_used_entries = 0;
|
|
|
|
desc->large_page_addr = -1;
|
|
|
|
desc->large_page_mask = -1;
|
|
|
|
desc->vindex = 0;
|
|
|
|
memset(fast->table, -1, sizeof_tlb(fast));
|
|
|
|
memset(desc->vtable, -1, sizeof(desc->vtable));
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 23:36:01 +01:00
|
|
|
static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx,
|
|
|
|
int64_t now)
|
2019-12-07 21:08:04 +01:00
|
|
|
{
|
|
|
|
CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx];
|
|
|
|
CPUTLBDescFast *fast = &env_tlb(env)->f[mmu_idx];
|
|
|
|
|
2019-12-07 23:36:01 +01:00
|
|
|
tlb_mmu_resize_locked(desc, fast, now);
|
2019-12-07 21:08:04 +01:00
|
|
|
tlb_mmu_flush_locked(desc, fast);
|
|
|
|
}
|
|
|
|
|
2019-12-07 22:22:19 +01:00
|
|
|
static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now)
|
|
|
|
{
|
|
|
|
size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS;
|
|
|
|
|
|
|
|
tlb_window_reset(desc, now, 0);
|
|
|
|
desc->n_used_entries = 0;
|
|
|
|
fast->mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS;
|
|
|
|
fast->table = g_new(CPUTLBEntry, n_entries);
|
2022-08-19 23:20:37 +02:00
|
|
|
desc->fulltlb = g_new(CPUTLBEntryFull, n_entries);
|
2020-01-09 01:23:56 +01:00
|
|
|
tlb_mmu_flush_locked(desc, fast);
|
2019-12-07 22:22:19 +01:00
|
|
|
}
|
|
|
|
|
2019-01-16 18:01:13 +01:00
|
|
|
static inline void tlb_n_used_entries_inc(CPUArchState *env, uintptr_t mmu_idx)
|
|
|
|
{
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->d[mmu_idx].n_used_entries++;
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void tlb_n_used_entries_dec(CPUArchState *env, uintptr_t mmu_idx)
|
|
|
|
{
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->d[mmu_idx].n_used_entries--;
|
2019-01-16 18:01:13 +01:00
|
|
|
}
|
|
|
|
|
2018-10-09 19:45:54 +02:00
|
|
|
void tlb_init(CPUState *cpu)
|
|
|
|
{
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
CPUArchState *env = cpu->env_ptr;
|
2019-12-07 22:22:19 +01:00
|
|
|
int64_t now = get_clock_realtime();
|
|
|
|
int i;
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_init(&env_tlb(env)->c.lock);
|
2018-10-20 21:04:57 +02:00
|
|
|
|
2020-01-09 01:23:56 +01:00
|
|
|
/* All tlbs are initialized flushed. */
|
|
|
|
env_tlb(env)->c.dirty = 0;
|
2019-01-16 18:01:13 +01:00
|
|
|
|
2019-12-07 22:22:19 +01:00
|
|
|
for (i = 0; i < NB_MMU_MODES; i++) {
|
|
|
|
tlb_mmu_init(&env_tlb(env)->d[i], &env_tlb(env)->f[i], now);
|
|
|
|
}
|
2018-10-09 19:45:54 +02:00
|
|
|
}
|
|
|
|
|
2020-06-12 21:02:26 +02:00
|
|
|
void tlb_destroy(CPUState *cpu)
|
|
|
|
{
|
|
|
|
CPUArchState *env = cpu->env_ptr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
qemu_spin_destroy(&env_tlb(env)->c.lock);
|
|
|
|
for (i = 0; i < NB_MMU_MODES; i++) {
|
|
|
|
CPUTLBDesc *desc = &env_tlb(env)->d[i];
|
|
|
|
CPUTLBDescFast *fast = &env_tlb(env)->f[i];
|
|
|
|
|
|
|
|
g_free(fast->table);
|
2022-08-19 23:20:37 +02:00
|
|
|
g_free(desc->fulltlb);
|
2020-06-12 21:02:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-23 19:29:22 +01:00
|
|
|
/* flush_all_helper: run fn across all cpus
|
|
|
|
*
|
|
|
|
* If the wait flag is set then the src cpu's helper will be queued as
|
|
|
|
* "safe" work and the loop exited creating a synchronisation point
|
|
|
|
* where all queued work will be finished before execution starts
|
|
|
|
* again.
|
|
|
|
*/
|
|
|
|
static void flush_all_helper(CPUState *src, run_on_cpu_func fn,
|
|
|
|
run_on_cpu_data d)
|
|
|
|
{
|
|
|
|
CPUState *cpu;
|
|
|
|
|
|
|
|
CPU_FOREACH(cpu) {
|
|
|
|
if (cpu != src) {
|
|
|
|
async_run_on_cpu(cpu, fn, d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-19 23:36:43 +02:00
|
|
|
void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
|
2017-07-06 20:42:26 +02:00
|
|
|
{
|
|
|
|
CPUState *cpu;
|
2018-10-19 23:36:43 +02:00
|
|
|
size_t full = 0, part = 0, elide = 0;
|
2017-07-06 20:42:26 +02:00
|
|
|
|
|
|
|
CPU_FOREACH(cpu) {
|
|
|
|
CPUArchState *env = cpu->env_ptr;
|
|
|
|
|
2020-09-23 12:56:46 +02:00
|
|
|
full += qatomic_read(&env_tlb(env)->c.full_flush_count);
|
|
|
|
part += qatomic_read(&env_tlb(env)->c.part_flush_count);
|
|
|
|
elide += qatomic_read(&env_tlb(env)->c.elide_flush_count);
|
2017-07-06 20:42:26 +02:00
|
|
|
}
|
2018-10-19 23:36:43 +02:00
|
|
|
*pfull = full;
|
|
|
|
*ppart = part;
|
|
|
|
*pelide = elide;
|
2017-07-06 20:42:26 +02:00
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2017-02-23 19:29:20 +01:00
|
|
|
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
2015-08-25 16:45:09 +02:00
|
|
|
{
|
|
|
|
CPUArchState *env = cpu->env_ptr;
|
2018-10-20 21:04:57 +02:00
|
|
|
uint16_t asked = data.host_int;
|
|
|
|
uint16_t all_dirty, work, to_clean;
|
2019-12-07 23:36:01 +01:00
|
|
|
int64_t now = get_clock_realtime();
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2017-02-23 19:29:16 +01:00
|
|
|
assert_cpu_is_self(cpu);
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2018-10-20 21:04:57 +02:00
|
|
|
tlb_debug("mmu_idx:0x%04" PRIx16 "\n", asked);
|
2017-02-23 19:29:20 +01:00
|
|
|
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_lock(&env_tlb(env)->c.lock);
|
2018-10-20 22:54:46 +02:00
|
|
|
|
2019-03-22 21:52:09 +01:00
|
|
|
all_dirty = env_tlb(env)->c.dirty;
|
2018-10-20 21:04:57 +02:00
|
|
|
to_clean = asked & all_dirty;
|
|
|
|
all_dirty &= ~to_clean;
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->c.dirty = all_dirty;
|
2018-10-20 21:04:57 +02:00
|
|
|
|
|
|
|
for (work = to_clean; work != 0; work &= work - 1) {
|
|
|
|
int mmu_idx = ctz32(work);
|
2019-12-07 23:36:01 +01:00
|
|
|
tlb_flush_one_mmuidx_locked(env, mmu_idx, now);
|
2015-08-25 16:45:09 +02:00
|
|
|
}
|
2018-10-20 21:04:57 +02:00
|
|
|
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2022-08-15 22:13:05 +02:00
|
|
|
tcg_flush_jmp_cache(cpu);
|
2018-10-23 07:01:01 +02:00
|
|
|
|
2018-10-20 21:04:57 +02:00
|
|
|
if (to_clean == ALL_MMUIDX_BITS) {
|
2020-09-23 12:56:46 +02:00
|
|
|
qatomic_set(&env_tlb(env)->c.full_flush_count,
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->c.full_flush_count + 1);
|
2018-10-19 23:36:43 +02:00
|
|
|
} else {
|
2020-09-23 12:56:46 +02:00
|
|
|
qatomic_set(&env_tlb(env)->c.part_flush_count,
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->c.part_flush_count + ctpop16(to_clean));
|
2018-10-20 21:04:57 +02:00
|
|
|
if (to_clean != asked) {
|
2020-09-23 12:56:46 +02:00
|
|
|
qatomic_set(&env_tlb(env)->c.elide_flush_count,
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->c.elide_flush_count +
|
2018-10-20 21:04:57 +02:00
|
|
|
ctpop16(asked & ~to_clean));
|
|
|
|
}
|
2018-10-23 07:01:01 +02:00
|
|
|
}
|
2015-08-25 16:45:09 +02:00
|
|
|
}
|
|
|
|
|
2017-02-23 19:29:19 +01:00
|
|
|
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
|
2015-08-25 16:45:09 +02:00
|
|
|
{
|
2017-02-23 19:29:20 +01:00
|
|
|
tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap);
|
|
|
|
|
2018-10-23 07:01:01 +02:00
|
|
|
if (cpu->created && !qemu_cpu_is_self(cpu)) {
|
2018-10-23 07:58:03 +02:00
|
|
|
async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work,
|
|
|
|
RUN_ON_CPU_HOST_INT(idxmap));
|
2017-02-23 19:29:20 +01:00
|
|
|
} else {
|
2018-10-20 22:54:46 +02:00
|
|
|
tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap));
|
2017-02-23 19:29:20 +01:00
|
|
|
}
|
2015-08-25 16:45:09 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 07:01:01 +02:00
|
|
|
void tlb_flush(CPUState *cpu)
|
|
|
|
{
|
|
|
|
tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS);
|
|
|
|
}
|
|
|
|
|
2017-02-23 19:29:22 +01:00
|
|
|
void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap)
|
|
|
|
{
|
|
|
|
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
|
|
|
|
|
|
|
|
tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap);
|
|
|
|
|
|
|
|
flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap));
|
|
|
|
fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap));
|
|
|
|
}
|
|
|
|
|
2018-10-23 07:01:01 +02:00
|
|
|
void tlb_flush_all_cpus(CPUState *src_cpu)
|
|
|
|
{
|
|
|
|
tlb_flush_by_mmuidx_all_cpus(src_cpu, ALL_MMUIDX_BITS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap)
|
2017-02-23 19:29:22 +01:00
|
|
|
{
|
|
|
|
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
|
|
|
|
|
|
|
|
tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap);
|
|
|
|
|
|
|
|
flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap));
|
|
|
|
async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap));
|
|
|
|
}
|
|
|
|
|
2018-10-23 07:01:01 +02:00
|
|
|
void tlb_flush_all_cpus_synced(CPUState *src_cpu)
|
|
|
|
{
|
|
|
|
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, ALL_MMUIDX_BITS);
|
|
|
|
}
|
|
|
|
|
2020-10-16 23:07:53 +02:00
|
|
|
static bool tlb_hit_page_mask_anyprot(CPUTLBEntry *tlb_entry,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr page, vaddr mask)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
|
|
|
page &= mask;
|
|
|
|
mask &= TARGET_PAGE_MASK | TLB_INVALID_MASK;
|
|
|
|
|
|
|
|
return (page == (tlb_entry->addr_read & mask) ||
|
|
|
|
page == (tlb_addr_write(tlb_entry) & mask) ||
|
|
|
|
page == (tlb_entry->addr_code & mask));
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, vaddr page)
|
2018-06-29 22:07:08 +02:00
|
|
|
{
|
2020-10-16 23:07:53 +02:00
|
|
|
return tlb_hit_page_mask_anyprot(tlb_entry, page, -1);
|
2018-06-29 22:07:08 +02:00
|
|
|
}
|
2017-02-23 19:29:22 +01:00
|
|
|
|
2019-01-16 18:01:12 +01:00
|
|
|
/**
|
|
|
|
* tlb_entry_is_empty - return true if the entry is not in use
|
|
|
|
* @te: pointer to CPUTLBEntry
|
|
|
|
*/
|
|
|
|
static inline bool tlb_entry_is_empty(const CPUTLBEntry *te)
|
|
|
|
{
|
|
|
|
return te->addr_read == -1 && te->addr_write == -1 && te->addr_code == -1;
|
|
|
|
}
|
|
|
|
|
2018-10-23 04:57:11 +02:00
|
|
|
/* Called with tlb_c.lock held */
|
2020-10-16 23:07:53 +02:00
|
|
|
static bool tlb_flush_entry_mask_locked(CPUTLBEntry *tlb_entry,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr page,
|
|
|
|
vaddr mask)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2020-10-16 23:07:53 +02:00
|
|
|
if (tlb_hit_page_mask_anyprot(tlb_entry, page, mask)) {
|
2013-12-06 22:44:51 +01:00
|
|
|
memset(tlb_entry, -1, sizeof(*tlb_entry));
|
2019-01-16 18:01:13 +01:00
|
|
|
return true;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
2019-01-16 18:01:13 +01:00
|
|
|
return false;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, vaddr page)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
|
|
|
return tlb_flush_entry_mask_locked(tlb_entry, page, -1);
|
|
|
|
}
|
|
|
|
|
2018-10-23 04:57:11 +02:00
|
|
|
/* Called with tlb_c.lock held */
|
2020-10-16 23:07:53 +02:00
|
|
|
static void tlb_flush_vtlb_page_mask_locked(CPUArchState *env, int mmu_idx,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr page,
|
|
|
|
vaddr mask)
|
2018-06-29 22:07:08 +02:00
|
|
|
{
|
2019-03-22 21:52:09 +01:00
|
|
|
CPUTLBDesc *d = &env_tlb(env)->d[mmu_idx];
|
2018-06-29 22:07:08 +02:00
|
|
|
int k;
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
|
2019-03-23 00:07:18 +01:00
|
|
|
assert_cpu_is_self(env_cpu(env));
|
2018-06-29 22:07:08 +02:00
|
|
|
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
2020-10-16 23:07:53 +02:00
|
|
|
if (tlb_flush_entry_mask_locked(&d->vtable[k], page, mask)) {
|
2019-01-16 18:01:13 +01:00
|
|
|
tlb_n_used_entries_dec(env, mmu_idx);
|
|
|
|
}
|
2018-06-29 22:07:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-16 23:07:53 +02:00
|
|
|
static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr page)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
|
|
|
tlb_flush_vtlb_page_mask_locked(env, mmu_idx, page, -1);
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
static void tlb_flush_page_locked(CPUArchState *env, int midx, vaddr page)
|
2018-10-17 20:48:40 +02:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr lp_addr = env_tlb(env)->d[midx].large_page_addr;
|
|
|
|
vaddr lp_mask = env_tlb(env)->d[midx].large_page_mask;
|
2018-10-17 20:48:40 +02:00
|
|
|
|
|
|
|
/* Check if we need to flush due to large pages. */
|
|
|
|
if ((page & lp_mask) == lp_addr) {
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("forcing full flush midx %d (%"
|
|
|
|
VADDR_PRIx "/%" VADDR_PRIx ")\n",
|
2018-10-17 20:48:40 +02:00
|
|
|
midx, lp_addr, lp_mask);
|
2019-12-07 23:36:01 +01:00
|
|
|
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
|
2018-10-17 20:48:40 +02:00
|
|
|
} else {
|
2019-01-16 18:01:13 +01:00
|
|
|
if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) {
|
|
|
|
tlb_n_used_entries_dec(env, midx);
|
|
|
|
}
|
2018-10-17 20:48:40 +02:00
|
|
|
tlb_flush_vtlb_page_locked(env, midx, page);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-11 14:53:30 +01:00
|
|
|
/**
|
|
|
|
* tlb_flush_page_by_mmuidx_async_0:
|
|
|
|
* @cpu: cpu on which to flush
|
|
|
|
* @addr: page of virtual address to flush
|
|
|
|
* @idxmap: set of mmu_idx to flush
|
|
|
|
*
|
|
|
|
* Helper for tlb_flush_page_by_mmuidx and friends, flush one page
|
|
|
|
* at @addr from the tlbs indicated by @idxmap from @cpu.
|
2017-02-23 19:29:20 +01:00
|
|
|
*/
|
2019-11-11 14:53:30 +01:00
|
|
|
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr,
|
2019-11-11 14:53:30 +01:00
|
|
|
uint16_t idxmap)
|
2015-08-25 16:45:09 +02:00
|
|
|
{
|
|
|
|
CPUArchState *env = cpu->env_ptr;
|
2017-02-23 19:29:20 +01:00
|
|
|
int mmu_idx;
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2017-02-23 19:29:16 +01:00
|
|
|
assert_cpu_is_self(cpu);
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("page addr: %" VADDR_PRIx " mmu_map:0x%x\n", addr, idxmap);
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_lock(&env_tlb(env)->c.lock);
|
2017-02-23 19:29:19 +01:00
|
|
|
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
2019-11-11 14:53:30 +01:00
|
|
|
if ((idxmap >> mmu_idx) & 1) {
|
2018-10-17 20:48:40 +02:00
|
|
|
tlb_flush_page_locked(env, mmu_idx, addr);
|
2015-08-25 16:45:09 +02:00
|
|
|
}
|
|
|
|
}
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
2015-08-25 16:45:09 +02:00
|
|
|
|
2022-09-29 19:51:21 +02:00
|
|
|
/*
|
|
|
|
* Discard jump cache entries for any tb which might potentially
|
|
|
|
* overlap the flushed page, which includes the previous.
|
|
|
|
*/
|
|
|
|
tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE);
|
|
|
|
tb_jmp_cache_clear_page(cpu, addr);
|
2015-08-25 16:45:09 +02:00
|
|
|
}
|
|
|
|
|
2019-11-11 14:53:30 +01:00
|
|
|
/**
|
|
|
|
* tlb_flush_page_by_mmuidx_async_1:
|
|
|
|
* @cpu: cpu on which to flush
|
|
|
|
* @data: encoded addr + idxmap
|
|
|
|
*
|
|
|
|
* Helper for tlb_flush_page_by_mmuidx and friends, called through
|
|
|
|
* async_run_on_cpu. The idxmap parameter is encoded in the page
|
|
|
|
* offset of the target_ptr field. This limits the set of mmu_idx
|
|
|
|
* that can be passed via this method.
|
|
|
|
*/
|
|
|
|
static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu,
|
|
|
|
run_on_cpu_data data)
|
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr_and_idxmap = data.target_ptr;
|
|
|
|
vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK;
|
2019-11-11 14:53:30 +01:00
|
|
|
uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
|
|
|
|
|
|
|
|
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr;
|
2019-11-11 14:53:30 +01:00
|
|
|
uint16_t idxmap;
|
|
|
|
} TLBFlushPageByMMUIdxData;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tlb_flush_page_by_mmuidx_async_2:
|
|
|
|
* @cpu: cpu on which to flush
|
|
|
|
* @data: allocated addr + idxmap
|
|
|
|
*
|
|
|
|
* Helper for tlb_flush_page_by_mmuidx and friends, called through
|
|
|
|
* async_run_on_cpu. The addr+idxmap parameters are stored in a
|
|
|
|
* TLBFlushPageByMMUIdxData structure that has been allocated
|
|
|
|
* specifically for this helper. Free the structure when done.
|
|
|
|
*/
|
|
|
|
static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu,
|
|
|
|
run_on_cpu_data data)
|
2017-02-23 19:29:20 +01:00
|
|
|
{
|
2019-11-11 14:53:30 +01:00
|
|
|
TLBFlushPageByMMUIdxData *d = data.host_ptr;
|
|
|
|
|
|
|
|
tlb_flush_page_by_mmuidx_async_0(cpu, d->addr, d->idxmap);
|
|
|
|
g_free(d);
|
|
|
|
}
|
2017-02-23 19:29:20 +01:00
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap)
|
2019-11-11 14:53:30 +01:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("addr: %" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap);
|
2017-02-23 19:29:20 +01:00
|
|
|
|
|
|
|
/* This should already be page aligned */
|
2019-11-11 14:53:30 +01:00
|
|
|
addr &= TARGET_PAGE_MASK;
|
2017-02-23 19:29:20 +01:00
|
|
|
|
2019-11-11 14:53:30 +01:00
|
|
|
if (qemu_cpu_is_self(cpu)) {
|
|
|
|
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
|
|
|
|
} else if (idxmap < TARGET_PAGE_SIZE) {
|
|
|
|
/*
|
|
|
|
* Most targets have only a few mmu_idx. In the case where
|
|
|
|
* we can stuff idxmap into the low TARGET_PAGE_BITS, avoid
|
|
|
|
* allocating memory for this operation.
|
|
|
|
*/
|
|
|
|
async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1,
|
|
|
|
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
2017-02-23 19:29:20 +01:00
|
|
|
} else {
|
2019-11-11 14:53:30 +01:00
|
|
|
TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1);
|
|
|
|
|
|
|
|
/* Otherwise allocate a structure, freed by the worker. */
|
|
|
|
d->addr = addr;
|
|
|
|
d->idxmap = idxmap;
|
|
|
|
async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2,
|
|
|
|
RUN_ON_CPU_HOST_PTR(d));
|
2017-02-23 19:29:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_page(CPUState *cpu, vaddr addr)
|
2018-10-19 23:25:09 +02:00
|
|
|
{
|
|
|
|
tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS);
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, vaddr addr,
|
2017-02-23 19:29:22 +01:00
|
|
|
uint16_t idxmap)
|
2017-02-23 19:29:18 +01:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("addr: %" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
|
2017-02-23 19:29:22 +01:00
|
|
|
|
|
|
|
/* This should already be page aligned */
|
2019-11-11 14:53:30 +01:00
|
|
|
addr &= TARGET_PAGE_MASK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate memory to hold addr+idxmap only when needed.
|
|
|
|
* See tlb_flush_page_by_mmuidx for details.
|
|
|
|
*/
|
|
|
|
if (idxmap < TARGET_PAGE_SIZE) {
|
|
|
|
flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1,
|
|
|
|
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
|
|
|
} else {
|
|
|
|
CPUState *dst_cpu;
|
|
|
|
|
|
|
|
/* Allocate a separate data block for each destination cpu. */
|
|
|
|
CPU_FOREACH(dst_cpu) {
|
|
|
|
if (dst_cpu != src_cpu) {
|
|
|
|
TLBFlushPageByMMUIdxData *d
|
|
|
|
= g_new(TLBFlushPageByMMUIdxData, 1);
|
|
|
|
|
|
|
|
d->addr = addr;
|
|
|
|
d->idxmap = idxmap;
|
|
|
|
async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2,
|
|
|
|
RUN_ON_CPU_HOST_PTR(d));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-23 19:29:22 +01:00
|
|
|
|
2019-11-11 14:53:30 +01:00
|
|
|
tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap);
|
2017-02-23 19:29:22 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_page_all_cpus(CPUState *src, vaddr addr)
|
2018-10-19 23:25:09 +02:00
|
|
|
{
|
|
|
|
tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS);
|
|
|
|
}
|
|
|
|
|
2017-02-23 19:29:22 +01:00
|
|
|
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr,
|
2018-10-17 20:48:40 +02:00
|
|
|
uint16_t idxmap)
|
2017-02-23 19:29:22 +01:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("addr: %" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
|
2017-02-23 19:29:22 +01:00
|
|
|
|
|
|
|
/* This should already be page aligned */
|
2019-11-11 14:53:30 +01:00
|
|
|
addr &= TARGET_PAGE_MASK;
|
2017-02-23 19:29:22 +01:00
|
|
|
|
2019-11-11 14:53:30 +01:00
|
|
|
/*
|
|
|
|
* Allocate memory to hold addr+idxmap only when needed.
|
|
|
|
* See tlb_flush_page_by_mmuidx for details.
|
|
|
|
*/
|
|
|
|
if (idxmap < TARGET_PAGE_SIZE) {
|
|
|
|
flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1,
|
|
|
|
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
|
|
|
async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_1,
|
|
|
|
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
|
|
|
} else {
|
|
|
|
CPUState *dst_cpu;
|
|
|
|
TLBFlushPageByMMUIdxData *d;
|
|
|
|
|
|
|
|
/* Allocate a separate data block for each destination cpu. */
|
|
|
|
CPU_FOREACH(dst_cpu) {
|
|
|
|
if (dst_cpu != src_cpu) {
|
|
|
|
d = g_new(TLBFlushPageByMMUIdxData, 1);
|
|
|
|
d->addr = addr;
|
|
|
|
d->idxmap = idxmap;
|
|
|
|
async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2,
|
|
|
|
RUN_ON_CPU_HOST_PTR(d));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = g_new(TLBFlushPageByMMUIdxData, 1);
|
|
|
|
d->addr = addr;
|
|
|
|
d->idxmap = idxmap;
|
|
|
|
async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_2,
|
|
|
|
RUN_ON_CPU_HOST_PTR(d));
|
|
|
|
}
|
2017-02-23 19:29:22 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr)
|
2017-02-23 19:29:22 +01:00
|
|
|
{
|
2018-10-19 23:25:09 +02:00
|
|
|
tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS);
|
2017-02-23 19:29:18 +01:00
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:11 +02:00
|
|
|
static void tlb_flush_range_locked(CPUArchState *env, int midx,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr, vaddr len,
|
2021-05-09 17:16:11 +02:00
|
|
|
unsigned bits)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
|
|
|
CPUTLBDesc *d = &env_tlb(env)->d[midx];
|
|
|
|
CPUTLBDescFast *f = &env_tlb(env)->f[midx];
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr mask = MAKE_64BIT_MASK(0, bits);
|
2020-10-16 23:07:53 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If @bits is smaller than the tlb size, there may be multiple entries
|
|
|
|
* within the TLB; otherwise all addresses that match under @mask hit
|
|
|
|
* the same TLB entry.
|
|
|
|
* TODO: Perhaps allow bits to be a few bits less than the size.
|
|
|
|
* For now, just flush the entire TLB.
|
2021-05-09 17:16:11 +02:00
|
|
|
*
|
|
|
|
* If @len is larger than the tlb size, then it will take longer to
|
|
|
|
* test all of the entries in the TLB than it will to flush it all.
|
2020-10-16 23:07:53 +02:00
|
|
|
*/
|
2021-05-09 17:16:11 +02:00
|
|
|
if (mask < f->mask || len > f->mask) {
|
2020-10-16 23:07:53 +02:00
|
|
|
tlb_debug("forcing full flush midx %d ("
|
2023-06-21 15:56:22 +02:00
|
|
|
"%" VADDR_PRIx "/%" VADDR_PRIx "+%" VADDR_PRIx ")\n",
|
2021-05-09 17:16:11 +02:00
|
|
|
midx, addr, mask, len);
|
2020-10-16 23:07:53 +02:00
|
|
|
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:11 +02:00
|
|
|
/*
|
|
|
|
* Check if we need to flush due to large pages.
|
|
|
|
* Because large_page_mask contains all 1's from the msb,
|
|
|
|
* we only need to test the end of the range.
|
|
|
|
*/
|
|
|
|
if (((addr + len - 1) & d->large_page_mask) == d->large_page_addr) {
|
2020-10-16 23:07:53 +02:00
|
|
|
tlb_debug("forcing full flush midx %d ("
|
2023-06-21 15:56:22 +02:00
|
|
|
"%" VADDR_PRIx "/%" VADDR_PRIx ")\n",
|
2020-10-16 23:07:53 +02:00
|
|
|
midx, d->large_page_addr, d->large_page_mask);
|
|
|
|
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
for (vaddr i = 0; i < len; i += TARGET_PAGE_SIZE) {
|
|
|
|
vaddr page = addr + i;
|
2021-05-09 17:16:11 +02:00
|
|
|
CPUTLBEntry *entry = tlb_entry(env, midx, page);
|
|
|
|
|
|
|
|
if (tlb_flush_entry_mask_locked(entry, page, mask)) {
|
|
|
|
tlb_n_used_entries_dec(env, midx);
|
|
|
|
}
|
|
|
|
tlb_flush_vtlb_page_mask_locked(env, midx, page, mask);
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr;
|
|
|
|
vaddr len;
|
2020-10-16 23:07:53 +02:00
|
|
|
uint16_t idxmap;
|
|
|
|
uint16_t bits;
|
2021-05-09 17:16:12 +02:00
|
|
|
} TLBFlushRangeData;
|
2020-10-16 23:07:53 +02:00
|
|
|
|
2021-05-09 17:16:16 +02:00
|
|
|
static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
|
|
|
|
TLBFlushRangeData d)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
|
|
|
CPUArchState *env = cpu->env_ptr;
|
|
|
|
int mmu_idx;
|
|
|
|
|
|
|
|
assert_cpu_is_self(cpu);
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("range: %" VADDR_PRIx "/%u+%" VADDR_PRIx " mmu_map:0x%x\n",
|
2021-05-09 17:16:11 +02:00
|
|
|
d.addr, d.bits, d.len, d.idxmap);
|
2020-10-16 23:07:53 +02:00
|
|
|
|
|
|
|
qemu_spin_lock(&env_tlb(env)->c.lock);
|
|
|
|
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
|
|
|
if ((d.idxmap >> mmu_idx) & 1) {
|
2021-05-09 17:16:11 +02:00
|
|
|
tlb_flush_range_locked(env, mmu_idx, d.addr, d.len, d.bits);
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
|
|
|
|
2022-01-10 17:47:53 +01:00
|
|
|
/*
|
|
|
|
* If the length is larger than the jump cache size, then it will take
|
|
|
|
* longer to clear each entry individually than it will to clear it all.
|
|
|
|
*/
|
|
|
|
if (d.len >= (TARGET_PAGE_SIZE * TB_JMP_CACHE_SIZE)) {
|
2022-08-15 22:13:05 +02:00
|
|
|
tcg_flush_jmp_cache(cpu);
|
2022-01-10 17:47:53 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-29 19:51:21 +02:00
|
|
|
/*
|
|
|
|
* Discard jump cache entries for any tb which might potentially
|
|
|
|
* overlap the flushed pages, which includes the previous.
|
|
|
|
*/
|
|
|
|
d.addr -= TARGET_PAGE_SIZE;
|
2023-06-21 15:56:22 +02:00
|
|
|
for (vaddr i = 0, n = d.len / TARGET_PAGE_SIZE + 1; i < n; i++) {
|
2022-09-29 19:51:21 +02:00
|
|
|
tb_jmp_cache_clear_page(cpu, d.addr);
|
|
|
|
d.addr += TARGET_PAGE_SIZE;
|
2021-05-09 17:16:11 +02:00
|
|
|
}
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:17 +02:00
|
|
|
static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu,
|
|
|
|
run_on_cpu_data data)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
2021-05-09 17:16:12 +02:00
|
|
|
TLBFlushRangeData *d = data.host_ptr;
|
2021-05-09 17:16:16 +02:00
|
|
|
tlb_flush_range_by_mmuidx_async_0(cpu, *d);
|
2020-10-16 23:07:53 +02:00
|
|
|
g_free(d);
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
|
|
|
|
vaddr len, uint16_t idxmap,
|
2021-05-09 17:16:13 +02:00
|
|
|
unsigned bits)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
2021-05-09 17:16:12 +02:00
|
|
|
TLBFlushRangeData d;
|
2020-10-16 23:07:53 +02:00
|
|
|
|
2021-05-09 17:16:13 +02:00
|
|
|
/*
|
|
|
|
* If all bits are significant, and len is small,
|
|
|
|
* this devolves to tlb_flush_page.
|
|
|
|
*/
|
|
|
|
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
2020-10-16 23:07:53 +02:00
|
|
|
tlb_flush_page_by_mmuidx(cpu, addr, idxmap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If no page bits are significant, this devolves to tlb_flush. */
|
|
|
|
if (bits < TARGET_PAGE_BITS) {
|
|
|
|
tlb_flush_by_mmuidx(cpu, idxmap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This should already be page aligned */
|
|
|
|
d.addr = addr & TARGET_PAGE_MASK;
|
2021-05-09 17:16:13 +02:00
|
|
|
d.len = len;
|
2020-10-16 23:07:53 +02:00
|
|
|
d.idxmap = idxmap;
|
|
|
|
d.bits = bits;
|
|
|
|
|
|
|
|
if (qemu_cpu_is_self(cpu)) {
|
2021-05-09 17:16:16 +02:00
|
|
|
tlb_flush_range_by_mmuidx_async_0(cpu, d);
|
2020-10-16 23:07:53 +02:00
|
|
|
} else {
|
|
|
|
/* Otherwise allocate a structure, freed by the worker. */
|
2021-05-09 17:16:12 +02:00
|
|
|
TLBFlushRangeData *p = g_memdup(&d, sizeof(d));
|
2021-05-09 17:16:17 +02:00
|
|
|
async_run_on_cpu(cpu, tlb_flush_range_by_mmuidx_async_1,
|
2020-10-16 23:07:53 +02:00
|
|
|
RUN_ON_CPU_HOST_PTR(p));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
|
2021-05-09 17:16:13 +02:00
|
|
|
uint16_t idxmap, unsigned bits)
|
|
|
|
{
|
|
|
|
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
|
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:14 +02:00
|
|
|
void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr, vaddr len,
|
2021-05-09 17:16:14 +02:00
|
|
|
uint16_t idxmap, unsigned bits)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
2021-05-09 17:16:12 +02:00
|
|
|
TLBFlushRangeData d;
|
2021-05-09 17:16:18 +02:00
|
|
|
CPUState *dst_cpu;
|
2020-10-16 23:07:53 +02:00
|
|
|
|
2021-05-09 17:16:14 +02:00
|
|
|
/*
|
|
|
|
* If all bits are significant, and len is small,
|
|
|
|
* this devolves to tlb_flush_page.
|
|
|
|
*/
|
|
|
|
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
2020-10-16 23:07:53 +02:00
|
|
|
tlb_flush_page_by_mmuidx_all_cpus(src_cpu, addr, idxmap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If no page bits are significant, this devolves to tlb_flush. */
|
|
|
|
if (bits < TARGET_PAGE_BITS) {
|
|
|
|
tlb_flush_by_mmuidx_all_cpus(src_cpu, idxmap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This should already be page aligned */
|
|
|
|
d.addr = addr & TARGET_PAGE_MASK;
|
2021-05-09 17:16:14 +02:00
|
|
|
d.len = len;
|
2020-10-16 23:07:53 +02:00
|
|
|
d.idxmap = idxmap;
|
|
|
|
d.bits = bits;
|
|
|
|
|
2021-05-09 17:16:18 +02:00
|
|
|
/* Allocate a separate data block for each destination cpu. */
|
|
|
|
CPU_FOREACH(dst_cpu) {
|
|
|
|
if (dst_cpu != src_cpu) {
|
|
|
|
TLBFlushRangeData *p = g_memdup(&d, sizeof(d));
|
|
|
|
async_run_on_cpu(dst_cpu,
|
2021-05-09 17:16:17 +02:00
|
|
|
tlb_flush_range_by_mmuidx_async_1,
|
2021-05-09 17:16:18 +02:00
|
|
|
RUN_ON_CPU_HOST_PTR(p));
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:16 +02:00
|
|
|
tlb_flush_range_by_mmuidx_async_0(src_cpu, d);
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:14 +02:00
|
|
|
void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr, uint16_t idxmap,
|
|
|
|
unsigned bits)
|
2021-05-09 17:16:14 +02:00
|
|
|
{
|
|
|
|
tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE,
|
|
|
|
idxmap, bits);
|
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:15 +02:00
|
|
|
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr,
|
|
|
|
vaddr len,
|
2021-05-09 17:16:15 +02:00
|
|
|
uint16_t idxmap,
|
|
|
|
unsigned bits)
|
2020-10-16 23:07:53 +02:00
|
|
|
{
|
2021-05-09 17:16:18 +02:00
|
|
|
TLBFlushRangeData d, *p;
|
|
|
|
CPUState *dst_cpu;
|
2020-10-16 23:07:53 +02:00
|
|
|
|
2021-05-09 17:16:15 +02:00
|
|
|
/*
|
|
|
|
* If all bits are significant, and len is small,
|
|
|
|
* this devolves to tlb_flush_page.
|
|
|
|
*/
|
|
|
|
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
2020-10-16 23:07:53 +02:00
|
|
|
tlb_flush_page_by_mmuidx_all_cpus_synced(src_cpu, addr, idxmap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If no page bits are significant, this devolves to tlb_flush. */
|
|
|
|
if (bits < TARGET_PAGE_BITS) {
|
|
|
|
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This should already be page aligned */
|
|
|
|
d.addr = addr & TARGET_PAGE_MASK;
|
2021-05-09 17:16:15 +02:00
|
|
|
d.len = len;
|
2020-10-16 23:07:53 +02:00
|
|
|
d.idxmap = idxmap;
|
|
|
|
d.bits = bits;
|
|
|
|
|
2021-05-09 17:16:18 +02:00
|
|
|
/* Allocate a separate data block for each destination cpu. */
|
|
|
|
CPU_FOREACH(dst_cpu) {
|
|
|
|
if (dst_cpu != src_cpu) {
|
|
|
|
p = g_memdup(&d, sizeof(d));
|
2021-05-09 17:16:17 +02:00
|
|
|
async_run_on_cpu(dst_cpu, tlb_flush_range_by_mmuidx_async_1,
|
2021-05-09 17:16:18 +02:00
|
|
|
RUN_ON_CPU_HOST_PTR(p));
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
}
|
2021-05-09 17:16:18 +02:00
|
|
|
|
|
|
|
p = g_memdup(&d, sizeof(d));
|
2021-05-09 17:16:17 +02:00
|
|
|
async_safe_run_on_cpu(src_cpu, tlb_flush_range_by_mmuidx_async_1,
|
2021-05-09 17:16:18 +02:00
|
|
|
RUN_ON_CPU_HOST_PTR(p));
|
2020-10-16 23:07:53 +02:00
|
|
|
}
|
|
|
|
|
2021-05-09 17:16:15 +02:00
|
|
|
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr,
|
2021-05-09 17:16:15 +02:00
|
|
|
uint16_t idxmap,
|
|
|
|
unsigned bits)
|
|
|
|
{
|
|
|
|
tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE,
|
|
|
|
idxmap, bits);
|
|
|
|
}
|
|
|
|
|
2012-04-09 18:50:52 +02:00
|
|
|
/* update the TLBs so that writes to code in the virtual page 'addr'
|
|
|
|
can be detected */
|
|
|
|
void tlb_protect_code(ram_addr_t ram_addr)
|
|
|
|
{
|
2022-08-15 22:00:57 +02:00
|
|
|
cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
|
|
|
|
TARGET_PAGE_SIZE,
|
2014-12-02 12:23:18 +01:00
|
|
|
DIRTY_MEMORY_CODE);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* update the TLB so that writes in physical page 'phys_addr' are no longer
|
|
|
|
tested for self modifying code */
|
2015-04-22 14:24:54 +02:00
|
|
|
void tlb_unprotect_code(ram_addr_t ram_addr)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2013-10-08 12:44:04 +02:00
|
|
|
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-23 19:29:21 +01:00
|
|
|
/*
|
|
|
|
* Dirty write flag handling
|
|
|
|
*
|
|
|
|
* When the TCG code writes to a location it looks up the address in
|
|
|
|
* the TLB and uses that data to compute the final address. If any of
|
|
|
|
* the lower bits of the address are set then the slow path is forced.
|
|
|
|
* There are a number of reasons to do this but for normal RAM the
|
|
|
|
* most usual is detecting writes to code regions which may invalidate
|
|
|
|
* generated code.
|
|
|
|
*
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
* Other vCPUs might be reading their TLBs during guest execution, so we update
|
2020-09-23 12:56:46 +02:00
|
|
|
* te->addr_write with qatomic_set. We don't need to worry about this for
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
* oversized guests as MTTCG is disabled for them.
|
2017-02-23 19:29:21 +01:00
|
|
|
*
|
2018-10-23 04:57:11 +02:00
|
|
|
* Called with tlb_c.lock held.
|
2017-02-23 19:29:21 +01:00
|
|
|
*/
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry,
|
|
|
|
uintptr_t start, uintptr_t length)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2017-02-23 19:29:21 +01:00
|
|
|
uintptr_t addr = tlb_entry->addr_write;
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2019-09-20 02:54:10 +02:00
|
|
|
if ((addr & (TLB_INVALID_MASK | TLB_MMIO |
|
|
|
|
TLB_DISCARD_WRITE | TLB_NOTDIRTY)) == 0) {
|
2017-02-23 19:29:21 +01:00
|
|
|
addr &= TARGET_PAGE_MASK;
|
|
|
|
addr += tlb_entry->addend;
|
2012-04-09 18:50:52 +02:00
|
|
|
if ((addr - start) < length) {
|
tcg: Widen CPUTLBEntry comparators to 64-bits
This makes CPUTLBEntry agnostic to the address size of the guest.
When 32-bit addresses are in effect, we can simply read the low
32 bits of the 64-bit field. Similarly when we need to update
the field for setting TLB_NOTDIRTY.
For TCG backends that could in theory be big-endian, but in
practice are not (arm, loongarch, riscv), use QEMU_BUILD_BUG_ON
to document and ensure this is not accidentally missed.
For s390x, which is always big-endian, use HOST_BIG_ENDIAN anyway,
to document the reason for the adjustment.
For sparc64 and ppc64, always perform a 64-bit load, and rely on
the following 32-bit comparison to ignore the high bits.
Rearrange mips and ppc if ladders for clarity.
Reviewed-by: Anton Johansson <anjo@rev.ng>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2023-03-24 21:02:59 +01:00
|
|
|
#if TARGET_LONG_BITS == 32
|
|
|
|
uint32_t *ptr_write = (uint32_t *)&tlb_entry->addr_write;
|
|
|
|
ptr_write += HOST_BIG_ENDIAN;
|
|
|
|
qatomic_set(ptr_write, *ptr_write | TLB_NOTDIRTY);
|
|
|
|
#elif TCG_OVERSIZED_GUEST
|
2012-04-09 18:50:52 +02:00
|
|
|
tlb_entry->addr_write |= TLB_NOTDIRTY;
|
2017-02-23 19:29:21 +01:00
|
|
|
#else
|
2020-09-23 12:56:46 +02:00
|
|
|
qatomic_set(&tlb_entry->addr_write,
|
tcg: Widen CPUTLBEntry comparators to 64-bits
This makes CPUTLBEntry agnostic to the address size of the guest.
When 32-bit addresses are in effect, we can simply read the low
32 bits of the 64-bit field. Similarly when we need to update
the field for setting TLB_NOTDIRTY.
For TCG backends that could in theory be big-endian, but in
practice are not (arm, loongarch, riscv), use QEMU_BUILD_BUG_ON
to document and ensure this is not accidentally missed.
For s390x, which is always big-endian, use HOST_BIG_ENDIAN anyway,
to document the reason for the adjustment.
For sparc64 and ppc64, always perform a 64-bit load, and rely on
the following 32-bit comparison to ignore the high bits.
Rearrange mips and ppc if ladders for clarity.
Reviewed-by: Anton Johansson <anjo@rev.ng>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2023-03-24 21:02:59 +01:00
|
|
|
tlb_entry->addr_write | TLB_NOTDIRTY);
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
#endif
|
2017-02-23 19:29:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
/*
|
2018-10-23 04:57:11 +02:00
|
|
|
* Called with tlb_c.lock held.
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
* Called only from the vCPU context, i.e. the TLB's owner thread.
|
|
|
|
*/
|
|
|
|
static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s)
|
2017-02-23 19:29:21 +01:00
|
|
|
{
|
|
|
|
*d = *s;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
2017-02-23 19:29:21 +01:00
|
|
|
/* This is a cross vCPU call (i.e. another vCPU resetting the flags of
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
* the target vCPU).
|
2018-10-23 04:57:11 +02:00
|
|
|
* We must take tlb_c.lock to avoid racing with another vCPU update. The only
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
* thing actually updated is the target TLB entry ->addr_write flags.
|
2017-02-23 19:29:21 +01:00
|
|
|
*/
|
2015-09-11 07:39:41 +02:00
|
|
|
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
|
|
|
CPUArchState *env;
|
|
|
|
|
2015-09-11 07:39:41 +02:00
|
|
|
int mmu_idx;
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2015-09-11 07:39:41 +02:00
|
|
|
env = cpu->env_ptr;
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_lock(&env_tlb(env)->c.lock);
|
2015-09-11 07:39:41 +02:00
|
|
|
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
|
|
|
unsigned int i;
|
2019-12-07 20:47:41 +01:00
|
|
|
unsigned int n = tlb_n_entries(&env_tlb(env)->f[mmu_idx]);
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2019-01-16 18:01:13 +01:00
|
|
|
for (i = 0; i < n; i++) {
|
2019-03-22 21:52:09 +01:00
|
|
|
tlb_reset_dirty_range_locked(&env_tlb(env)->f[mmu_idx].table[i],
|
|
|
|
start1, length);
|
2015-09-11 07:39:41 +02:00
|
|
|
}
|
implementing victim TLB for QEMU system emulated TLB
QEMU system mode page table walks are expensive. Taken by running QEMU
qemu-system-x86_64 system mode on Intel PIN , a TLB miss and walking a
4-level page tables in guest Linux OS takes ~450 X86 instructions on
average.
QEMU system mode TLB is implemented using a directly-mapped hashtable.
This structure suffers from conflict misses. Increasing the
associativity of the TLB may not be the solution to conflict misses as
all the ways may have to be walked in serial.
A victim TLB is a TLB used to hold translations evicted from the
primary TLB upon replacement. The victim TLB lies between the main TLB
and its refill path. Victim TLB is of greater associativity (fully
associative in this patch). It takes longer to lookup the victim TLB,
but its likely better than a full page table walk. The memory
translation path is changed as follows :
Before Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. TLB refill.
5. Do the memory access.
6. Return to code cache.
After Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. Victim TLB lookup.
5. If victim TLB misses, TLB refill
6. Do the memory access.
7. Return to code cache
The advantage is that victim TLB can offer more associativity to a
directly mapped TLB and thus potentially fewer page table walks while
still keeping the time taken to flush within reasonable limits.
However, placing a victim TLB before the refill path increase TLB
refill path as the victim TLB is consulted before the TLB refill. The
performance results demonstrate that the pros outweigh the cons.
some performance results taken on SPECINT2006 train
datasets and kernel boot and qemu configure script on an
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz Linux machine are shown in the
Google Doc link below.
https://docs.google.com/spreadsheets/d/1eiItzekZwNQOal_h-5iJmC4tMDi051m9qidi5_nwvH4/edit?usp=sharing
In summary, victim TLB improves the performance of qemu-system-x86_64 by
11% on average on SPECINT2006, kernelboot and qemu configscript and with
highest improvement of in 26% in 456.hmmer. And victim TLB does not result
in any performance degradation in any of the measured benchmarks. Furthermore,
the implemented victim TLB is architecture independent and is expected to
benefit other architectures in QEMU as well.
Although there are measurement fluctuations, the performance
improvement is very significant and by no means in the range of
noises.
Signed-off-by: Xin Tong <trent.tong@gmail.com>
Message-id: 1407202523-23553-1-git-send-email-trent.tong@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2014-08-05 03:35:23 +02:00
|
|
|
|
2015-09-11 07:39:41 +02:00
|
|
|
for (i = 0; i < CPU_VTLB_SIZE; i++) {
|
2019-03-22 21:52:09 +01:00
|
|
|
tlb_reset_dirty_range_locked(&env_tlb(env)->d[mmu_idx].vtable[i],
|
|
|
|
start1, length);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
}
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 04:57:11 +02:00
|
|
|
/* Called with tlb_c.lock held */
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
if (tlb_entry->addr_write == (addr | TLB_NOTDIRTY)) {
|
|
|
|
tlb_entry->addr_write = addr;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the TLB corresponding to virtual page vaddr
|
|
|
|
so that it is no longer dirty */
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_set_dirty(CPUState *cpu, vaddr addr)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2015-09-11 07:39:42 +02:00
|
|
|
CPUArchState *env = cpu->env_ptr;
|
2012-04-09 18:50:52 +02:00
|
|
|
int mmu_idx;
|
|
|
|
|
2017-02-23 19:29:16 +01:00
|
|
|
assert_cpu_is_self(cpu);
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
addr &= TARGET_PAGE_MASK;
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_lock(&env_tlb(env)->c.lock);
|
2012-04-09 18:50:52 +02:00
|
|
|
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, addr), addr);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
implementing victim TLB for QEMU system emulated TLB
QEMU system mode page table walks are expensive. Taken by running QEMU
qemu-system-x86_64 system mode on Intel PIN , a TLB miss and walking a
4-level page tables in guest Linux OS takes ~450 X86 instructions on
average.
QEMU system mode TLB is implemented using a directly-mapped hashtable.
This structure suffers from conflict misses. Increasing the
associativity of the TLB may not be the solution to conflict misses as
all the ways may have to be walked in serial.
A victim TLB is a TLB used to hold translations evicted from the
primary TLB upon replacement. The victim TLB lies between the main TLB
and its refill path. Victim TLB is of greater associativity (fully
associative in this patch). It takes longer to lookup the victim TLB,
but its likely better than a full page table walk. The memory
translation path is changed as follows :
Before Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. TLB refill.
5. Do the memory access.
6. Return to code cache.
After Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. Victim TLB lookup.
5. If victim TLB misses, TLB refill
6. Do the memory access.
7. Return to code cache
The advantage is that victim TLB can offer more associativity to a
directly mapped TLB and thus potentially fewer page table walks while
still keeping the time taken to flush within reasonable limits.
However, placing a victim TLB before the refill path increase TLB
refill path as the victim TLB is consulted before the TLB refill. The
performance results demonstrate that the pros outweigh the cons.
some performance results taken on SPECINT2006 train
datasets and kernel boot and qemu configure script on an
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz Linux machine are shown in the
Google Doc link below.
https://docs.google.com/spreadsheets/d/1eiItzekZwNQOal_h-5iJmC4tMDi051m9qidi5_nwvH4/edit?usp=sharing
In summary, victim TLB improves the performance of qemu-system-x86_64 by
11% on average on SPECINT2006, kernelboot and qemu configscript and with
highest improvement of in 26% in 456.hmmer. And victim TLB does not result
in any performance degradation in any of the measured benchmarks. Furthermore,
the implemented victim TLB is architecture independent and is expected to
benefit other architectures in QEMU as well.
Although there are measurement fluctuations, the performance
improvement is very significant and by no means in the range of
noises.
Signed-off-by: Xin Tong <trent.tong@gmail.com>
Message-id: 1407202523-23553-1-git-send-email-trent.tong@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2014-08-05 03:35:23 +02:00
|
|
|
|
|
|
|
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
|
|
|
int k;
|
|
|
|
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_set_dirty1_locked(&env_tlb(env)->d[mmu_idx].vtable[k], addr);
|
implementing victim TLB for QEMU system emulated TLB
QEMU system mode page table walks are expensive. Taken by running QEMU
qemu-system-x86_64 system mode on Intel PIN , a TLB miss and walking a
4-level page tables in guest Linux OS takes ~450 X86 instructions on
average.
QEMU system mode TLB is implemented using a directly-mapped hashtable.
This structure suffers from conflict misses. Increasing the
associativity of the TLB may not be the solution to conflict misses as
all the ways may have to be walked in serial.
A victim TLB is a TLB used to hold translations evicted from the
primary TLB upon replacement. The victim TLB lies between the main TLB
and its refill path. Victim TLB is of greater associativity (fully
associative in this patch). It takes longer to lookup the victim TLB,
but its likely better than a full page table walk. The memory
translation path is changed as follows :
Before Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. TLB refill.
5. Do the memory access.
6. Return to code cache.
After Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. Victim TLB lookup.
5. If victim TLB misses, TLB refill
6. Do the memory access.
7. Return to code cache
The advantage is that victim TLB can offer more associativity to a
directly mapped TLB and thus potentially fewer page table walks while
still keeping the time taken to flush within reasonable limits.
However, placing a victim TLB before the refill path increase TLB
refill path as the victim TLB is consulted before the TLB refill. The
performance results demonstrate that the pros outweigh the cons.
some performance results taken on SPECINT2006 train
datasets and kernel boot and qemu configure script on an
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz Linux machine are shown in the
Google Doc link below.
https://docs.google.com/spreadsheets/d/1eiItzekZwNQOal_h-5iJmC4tMDi051m9qidi5_nwvH4/edit?usp=sharing
In summary, victim TLB improves the performance of qemu-system-x86_64 by
11% on average on SPECINT2006, kernelboot and qemu configscript and with
highest improvement of in 26% in 456.hmmer. And victim TLB does not result
in any performance degradation in any of the measured benchmarks. Furthermore,
the implemented victim TLB is architecture independent and is expected to
benefit other architectures in QEMU as well.
Although there are measurement fluctuations, the performance
improvement is very significant and by no means in the range of
noises.
Signed-off-by: Xin Tong <trent.tong@gmail.com>
Message-id: 1407202523-23553-1-git-send-email-trent.tong@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2014-08-05 03:35:23 +02:00
|
|
|
}
|
|
|
|
}
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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. */
|
2018-10-17 20:48:40 +02:00
|
|
|
static void tlb_add_large_page(CPUArchState *env, int mmu_idx,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr, uint64_t size)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr lp_addr = env_tlb(env)->d[mmu_idx].large_page_addr;
|
|
|
|
vaddr lp_mask = ~(size - 1);
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
if (lp_addr == (vaddr)-1) {
|
2018-10-17 20:48:40 +02:00
|
|
|
/* No previous large page. */
|
2023-06-21 15:56:22 +02:00
|
|
|
lp_addr = addr;
|
2018-10-17 20:48:40 +02:00
|
|
|
} 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. */
|
2019-03-22 21:52:09 +01:00
|
|
|
lp_mask &= env_tlb(env)->d[mmu_idx].large_page_mask;
|
2023-06-21 15:56:22 +02:00
|
|
|
while (((lp_addr ^ addr) & lp_mask) != 0) {
|
2018-10-17 20:48:40 +02:00
|
|
|
lp_mask <<= 1;
|
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
2019-03-22 21:52:09 +01:00
|
|
|
env_tlb(env)->d[mmu_idx].large_page_addr = lp_addr & lp_mask;
|
|
|
|
env_tlb(env)->d[mmu_idx].large_page_mask = lp_mask;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
2022-08-20 01:33:23 +02:00
|
|
|
/*
|
|
|
|
* Add a new TLB entry. At most one entry for a given virtual address
|
2015-01-21 12:09:14 +01:00
|
|
|
* is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
|
|
|
|
* supplied size is only used by tlb_flush_page.
|
|
|
|
*
|
|
|
|
* Called from TCG-generated code, which is under an RCU read-side
|
|
|
|
* critical section.
|
|
|
|
*/
|
2022-08-20 01:33:23 +02:00
|
|
|
void tlb_set_page_full(CPUState *cpu, int mmu_idx,
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr, CPUTLBEntryFull *full)
|
2012-04-09 18:50:52 +02:00
|
|
|
{
|
2013-09-03 13:59:37 +02:00
|
|
|
CPUArchState *env = cpu->env_ptr;
|
2019-03-22 21:52:09 +01:00
|
|
|
CPUTLB *tlb = env_tlb(env);
|
|
|
|
CPUTLBDesc *desc = &tlb->d[mmu_idx];
|
2012-04-09 18:50:52 +02:00
|
|
|
MemoryRegionSection *section;
|
|
|
|
unsigned int index;
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr address;
|
|
|
|
vaddr write_address;
|
2012-04-09 18:50:52 +02:00
|
|
|
uintptr_t addend;
|
2018-06-29 22:07:08 +02:00
|
|
|
CPUTLBEntry *te, tn;
|
2018-06-26 18:50:41 +02:00
|
|
|
hwaddr iotlb, xlat, sz, paddr_page;
|
2023-06-21 15:56:22 +02:00
|
|
|
vaddr addr_page;
|
2022-08-20 01:33:23 +02:00
|
|
|
int asidx, wp_flags, prot;
|
2019-09-20 06:09:58 +02:00
|
|
|
bool is_ram, is_romd;
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2017-02-23 19:29:16 +01:00
|
|
|
assert_cpu_is_self(cpu);
|
2018-06-26 18:50:41 +02:00
|
|
|
|
2022-08-20 01:33:23 +02:00
|
|
|
if (full->lg_page_size <= TARGET_PAGE_BITS) {
|
2018-06-26 18:50:41 +02:00
|
|
|
sz = TARGET_PAGE_SIZE;
|
|
|
|
} else {
|
2022-08-20 01:33:23 +02:00
|
|
|
sz = (hwaddr)1 << full->lg_page_size;
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_add_large_page(env, mmu_idx, addr, sz);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
2023-06-21 15:56:22 +02:00
|
|
|
addr_page = addr & TARGET_PAGE_MASK;
|
2022-08-20 01:33:23 +02:00
|
|
|
paddr_page = full->phys_addr & TARGET_PAGE_MASK;
|
2013-05-24 12:59:37 +02:00
|
|
|
|
2022-08-20 01:33:23 +02:00
|
|
|
prot = full->prot;
|
|
|
|
asidx = cpu_asidx_from_attrs(cpu, full->attrs);
|
2018-06-26 18:50:41 +02:00
|
|
|
section = address_space_translate_for_iotlb(cpu, asidx, paddr_page,
|
2022-08-20 01:33:23 +02:00
|
|
|
&xlat, &sz, full->attrs, &prot);
|
2013-05-24 12:59:37 +02:00
|
|
|
assert(sz >= TARGET_PAGE_SIZE);
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_debug("vaddr=%" VADDR_PRIx " paddr=0x" HWADDR_FMT_plx
|
2016-03-15 15:30:24 +01:00
|
|
|
" prot=%x idx=%d\n",
|
2023-06-21 15:56:22 +02:00
|
|
|
addr, full->phys_addr, prot, mmu_idx);
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
address = addr_page;
|
2022-08-20 01:33:23 +02:00
|
|
|
if (full->lg_page_size < TARGET_PAGE_BITS) {
|
2019-08-24 00:12:32 +02:00
|
|
|
/* Repeat the MMU check and TLB fill on every access. */
|
|
|
|
address |= TLB_INVALID_MASK;
|
2018-06-26 18:50:41 +02:00
|
|
|
}
|
2022-08-20 01:33:23 +02:00
|
|
|
if (full->attrs.byte_swap) {
|
2019-09-10 21:47:39 +02:00
|
|
|
address |= TLB_BSWAP;
|
2019-08-23 20:36:56 +02:00
|
|
|
}
|
2019-09-20 06:09:58 +02:00
|
|
|
|
|
|
|
is_ram = memory_region_is_ram(section->mr);
|
|
|
|
is_romd = memory_region_is_romd(section->mr);
|
|
|
|
|
|
|
|
if (is_ram || is_romd) {
|
|
|
|
/* RAM and ROMD both have associated host memory. */
|
|
|
|
addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;
|
|
|
|
} else {
|
|
|
|
/* I/O does not; force the host address to NULL. */
|
2013-05-24 16:45:30 +02:00
|
|
|
addend = 0;
|
2019-09-20 06:09:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
write_address = address;
|
|
|
|
if (is_ram) {
|
|
|
|
iotlb = memory_region_get_ram_addr(section->mr) + xlat;
|
|
|
|
/*
|
|
|
|
* Computing is_clean is expensive; avoid all that unless
|
|
|
|
* the page is actually writable.
|
|
|
|
*/
|
|
|
|
if (prot & PAGE_WRITE) {
|
|
|
|
if (section->readonly) {
|
|
|
|
write_address |= TLB_DISCARD_WRITE;
|
|
|
|
} else if (cpu_physical_memory_is_clean(iotlb)) {
|
|
|
|
write_address |= TLB_NOTDIRTY;
|
|
|
|
}
|
|
|
|
}
|
2013-05-24 16:45:30 +02:00
|
|
|
} else {
|
2019-09-20 06:09:58 +02:00
|
|
|
/* I/O or ROMD */
|
|
|
|
iotlb = memory_region_section_get_iotlb(cpu, section) + xlat;
|
|
|
|
/*
|
|
|
|
* Writes to romd devices must go through MMIO to enable write.
|
|
|
|
* 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;
|
|
|
|
if (!is_romd) {
|
|
|
|
address = write_address;
|
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
wp_flags = cpu_watchpoint_address_matches(cpu, addr_page,
|
2019-08-24 18:51:09 +02:00
|
|
|
TARGET_PAGE_SIZE);
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
index = tlb_index(env, mmu_idx, addr_page);
|
|
|
|
te = tlb_entry(env, mmu_idx, addr_page);
|
2017-02-23 19:29:21 +01:00
|
|
|
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
/*
|
|
|
|
* Hold the TLB lock for the rest of the function. We could acquire/release
|
|
|
|
* the lock several times in the function, but it is faster to amortize the
|
|
|
|
* acquisition cost by acquiring it just once. Note that this leads to
|
|
|
|
* a longer critical section, but this is not a concern since the TLB lock
|
|
|
|
* is unlikely to be contended.
|
|
|
|
*/
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_lock(&tlb->c.lock);
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
|
2018-10-20 21:04:57 +02:00
|
|
|
/* Note that the tlb is no longer clean. */
|
2019-03-22 21:52:09 +01:00
|
|
|
tlb->c.dirty |= 1 << mmu_idx;
|
2018-10-20 21:04:57 +02:00
|
|
|
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
/* Make sure there's no cached translation for the new page. */
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_flush_vtlb_page_locked(env, mmu_idx, addr_page);
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
|
2018-06-29 22:07:08 +02:00
|
|
|
/*
|
|
|
|
* Only evict the old entry to the victim tlb if it's for a
|
|
|
|
* different page; otherwise just overwrite the stale data.
|
|
|
|
*/
|
2023-06-21 15:56:22 +02:00
|
|
|
if (!tlb_hit_page_anyprot(te, addr_page) && !tlb_entry_is_empty(te)) {
|
2019-03-22 21:52:09 +01:00
|
|
|
unsigned vidx = desc->vindex++ % CPU_VTLB_SIZE;
|
|
|
|
CPUTLBEntry *tv = &desc->vtable[vidx];
|
2017-02-23 19:29:21 +01:00
|
|
|
|
2018-06-29 22:07:08 +02:00
|
|
|
/* Evict the old entry into the victim tlb. */
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
copy_tlb_helper_locked(tv, te);
|
2022-08-19 23:20:37 +02:00
|
|
|
desc->vfulltlb[vidx] = desc->fulltlb[index];
|
2019-01-16 18:01:13 +01:00
|
|
|
tlb_n_used_entries_dec(env, mmu_idx);
|
2018-06-29 22:07:08 +02:00
|
|
|
}
|
implementing victim TLB for QEMU system emulated TLB
QEMU system mode page table walks are expensive. Taken by running QEMU
qemu-system-x86_64 system mode on Intel PIN , a TLB miss and walking a
4-level page tables in guest Linux OS takes ~450 X86 instructions on
average.
QEMU system mode TLB is implemented using a directly-mapped hashtable.
This structure suffers from conflict misses. Increasing the
associativity of the TLB may not be the solution to conflict misses as
all the ways may have to be walked in serial.
A victim TLB is a TLB used to hold translations evicted from the
primary TLB upon replacement. The victim TLB lies between the main TLB
and its refill path. Victim TLB is of greater associativity (fully
associative in this patch). It takes longer to lookup the victim TLB,
but its likely better than a full page table walk. The memory
translation path is changed as follows :
Before Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. TLB refill.
5. Do the memory access.
6. Return to code cache.
After Victim TLB:
1. Inline TLB lookup
2. Exit code cache on TLB miss.
3. Check for unaligned, IO accesses
4. Victim TLB lookup.
5. If victim TLB misses, TLB refill
6. Do the memory access.
7. Return to code cache
The advantage is that victim TLB can offer more associativity to a
directly mapped TLB and thus potentially fewer page table walks while
still keeping the time taken to flush within reasonable limits.
However, placing a victim TLB before the refill path increase TLB
refill path as the victim TLB is consulted before the TLB refill. The
performance results demonstrate that the pros outweigh the cons.
some performance results taken on SPECINT2006 train
datasets and kernel boot and qemu configure script on an
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz Linux machine are shown in the
Google Doc link below.
https://docs.google.com/spreadsheets/d/1eiItzekZwNQOal_h-5iJmC4tMDi051m9qidi5_nwvH4/edit?usp=sharing
In summary, victim TLB improves the performance of qemu-system-x86_64 by
11% on average on SPECINT2006, kernelboot and qemu configscript and with
highest improvement of in 26% in 456.hmmer. And victim TLB does not result
in any performance degradation in any of the measured benchmarks. Furthermore,
the implemented victim TLB is architecture independent and is expected to
benefit other architectures in QEMU as well.
Although there are measurement fluctuations, the performance
improvement is very significant and by no means in the range of
noises.
Signed-off-by: Xin Tong <trent.tong@gmail.com>
Message-id: 1407202523-23553-1-git-send-email-trent.tong@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2014-08-05 03:35:23 +02:00
|
|
|
|
|
|
|
/* refill the tlb */
|
2018-06-15 15:57:14 +02:00
|
|
|
/*
|
|
|
|
* At this point iotlb contains a physical section number in the lower
|
|
|
|
* TARGET_PAGE_BITS, and either
|
2019-09-20 06:09:58 +02:00
|
|
|
* + 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)
|
2018-06-26 18:50:41 +02:00
|
|
|
* We subtract the vaddr_page (which is page aligned and thus won't
|
2018-06-15 15:57:14 +02:00
|
|
|
* 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
|
|
|
|
* subtract here is that of the page base, and not the same as the
|
|
|
|
* vaddr we add back in io_readx()/io_writex()/get_page_addr_code().
|
|
|
|
*/
|
2022-08-20 01:33:23 +02:00
|
|
|
desc->fulltlb[index] = *full;
|
2023-06-21 15:56:22 +02:00
|
|
|
desc->fulltlb[index].xlat_section = iotlb - addr_page;
|
2022-08-20 01:33:23 +02:00
|
|
|
desc->fulltlb[index].phys_addr = paddr_page;
|
2017-02-23 19:29:21 +01:00
|
|
|
|
|
|
|
/* Now calculate the new entry */
|
2023-06-21 15:56:22 +02:00
|
|
|
tn.addend = addend - addr_page;
|
2012-04-09 18:50:52 +02:00
|
|
|
if (prot & PAGE_READ) {
|
2017-02-23 19:29:21 +01:00
|
|
|
tn.addr_read = address;
|
2019-08-24 18:51:09 +02:00
|
|
|
if (wp_flags & BP_MEM_READ) {
|
|
|
|
tn.addr_read |= TLB_WATCHPOINT;
|
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
} else {
|
2017-02-23 19:29:21 +01:00
|
|
|
tn.addr_read = -1;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (prot & PAGE_EXEC) {
|
2019-09-20 06:09:58 +02:00
|
|
|
tn.addr_code = address;
|
2012-04-09 18:50:52 +02:00
|
|
|
} else {
|
2017-02-23 19:29:21 +01:00
|
|
|
tn.addr_code = -1;
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
2017-02-23 19:29:21 +01:00
|
|
|
|
|
|
|
tn.addr_write = -1;
|
2012-04-09 18:50:52 +02:00
|
|
|
if (prot & PAGE_WRITE) {
|
2019-09-20 06:09:58 +02:00
|
|
|
tn.addr_write = write_address;
|
2017-10-16 22:23:57 +02:00
|
|
|
if (prot & PAGE_WRITE_INV) {
|
|
|
|
tn.addr_write |= TLB_INVALID_MASK;
|
|
|
|
}
|
2019-08-24 18:51:09 +02:00
|
|
|
if (wp_flags & BP_MEM_WRITE) {
|
|
|
|
tn.addr_write |= TLB_WATCHPOINT;
|
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
2017-02-23 19:29:21 +01:00
|
|
|
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
copy_tlb_helper_locked(te, &tn);
|
2019-01-16 18:01:13 +01:00
|
|
|
tlb_n_used_entries_inc(env, mmu_idx);
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_unlock(&tlb->c.lock);
|
2012-04-09 18:50:52 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr,
|
2022-08-20 01:33:23 +02:00
|
|
|
hwaddr paddr, MemTxAttrs attrs, int prot,
|
2023-06-21 15:56:22 +02:00
|
|
|
int mmu_idx, uint64_t size)
|
2022-08-20 01:33:23 +02:00
|
|
|
{
|
|
|
|
CPUTLBEntryFull full = {
|
|
|
|
.phys_addr = paddr,
|
|
|
|
.attrs = attrs,
|
|
|
|
.prot = prot,
|
|
|
|
.lg_page_size = ctz64(size)
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(is_power_of_2(size));
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_set_page_full(cpu, mmu_idx, addr, &full);
|
2022-08-20 01:33:23 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
void tlb_set_page(CPUState *cpu, vaddr addr,
|
2015-04-26 17:49:24 +02:00
|
|
|
hwaddr paddr, int prot,
|
2023-06-21 15:56:22 +02:00
|
|
|
int mmu_idx, uint64_t size)
|
2015-04-26 17:49:24 +02:00
|
|
|
{
|
2023-06-21 15:56:22 +02:00
|
|
|
tlb_set_page_with_attrs(cpu, addr, paddr, MEMTXATTRS_UNSPECIFIED,
|
2015-04-26 17:49:24 +02:00
|
|
|
prot, mmu_idx, size);
|
|
|
|
}
|
|
|
|
|
2019-04-03 04:07:11 +02:00
|
|
|
/*
|
|
|
|
* Note: tlb_fill() can trigger a resize of the TLB. This means that all of the
|
|
|
|
* caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must
|
|
|
|
* be discarded and looked up again (e.g. via tlb_entry()).
|
|
|
|
*/
|
2023-06-21 15:56:22 +02:00
|
|
|
static void tlb_fill(CPUState *cpu, vaddr addr, int size,
|
2019-04-03 04:07:11 +02:00
|
|
|
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is not a probe, so only valid return is success; failure
|
|
|
|
* should result in exception + longjmp to the cpu loop.
|
|
|
|
*/
|
2022-09-23 10:48:01 +02:00
|
|
|
ok = cpu->cc->tcg_ops->tlb_fill(cpu, addr, size,
|
|
|
|
access_type, mmu_idx, false, retaddr);
|
2019-04-03 04:07:11 +02:00
|
|
|
assert(ok);
|
|
|
|
}
|
|
|
|
|
2021-02-04 17:39:23 +01:00
|
|
|
static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr,
|
|
|
|
MMUAccessType access_type,
|
|
|
|
int mmu_idx, uintptr_t retaddr)
|
|
|
|
{
|
2022-09-23 10:48:01 +02:00
|
|
|
cpu->cc->tcg_ops->do_unaligned_access(cpu, addr, access_type,
|
|
|
|
mmu_idx, retaddr);
|
2021-02-04 17:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr,
|
|
|
|
vaddr addr, unsigned size,
|
|
|
|
MMUAccessType access_type,
|
|
|
|
int mmu_idx, MemTxAttrs attrs,
|
|
|
|
MemTxResult response,
|
|
|
|
uintptr_t retaddr)
|
|
|
|
{
|
|
|
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
|
|
|
|
|
|
|
if (!cpu->ignore_memory_transaction_failures &&
|
|
|
|
cc->tcg_ops->do_transaction_failed) {
|
|
|
|
cc->tcg_ops->do_transaction_failed(cpu, physaddr, addr, size,
|
|
|
|
access_type, mmu_idx, attrs,
|
|
|
|
response, retaddr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-19 23:20:37 +02:00
|
|
|
static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
|
2023-06-21 15:56:22 +02:00
|
|
|
int mmu_idx, vaddr addr, uintptr_t retaddr,
|
2019-08-23 20:36:53 +02:00
|
|
|
MMUAccessType access_type, MemOp op)
|
2016-07-09 03:51:28 +02:00
|
|
|
{
|
2019-03-23 00:07:18 +01:00
|
|
|
CPUState *cpu = env_cpu(env);
|
2018-06-15 15:57:14 +02:00
|
|
|
hwaddr mr_offset;
|
|
|
|
MemoryRegionSection *section;
|
|
|
|
MemoryRegion *mr;
|
2016-07-09 03:51:28 +02:00
|
|
|
uint64_t val;
|
2017-09-04 16:21:55 +02:00
|
|
|
MemTxResult r;
|
2016-07-09 03:51:28 +02:00
|
|
|
|
2022-08-19 23:20:37 +02:00
|
|
|
section = iotlb_to_section(cpu, full->xlat_section, full->attrs);
|
2018-06-15 15:57:14 +02:00
|
|
|
mr = section->mr;
|
2022-08-19 23:20:37 +02:00
|
|
|
mr_offset = (full->xlat_section & TARGET_PAGE_MASK) + addr;
|
2016-07-09 03:51:28 +02:00
|
|
|
cpu->mem_io_pc = retaddr;
|
2019-09-18 18:15:44 +02:00
|
|
|
if (!cpu->can_do_io) {
|
2016-07-09 03:51:28 +02:00
|
|
|
cpu_io_recompile(cpu, retaddr);
|
|
|
|
}
|
|
|
|
|
2022-11-18 04:59:16 +01:00
|
|
|
{
|
|
|
|
QEMU_IOTHREAD_LOCK_GUARD();
|
|
|
|
r = memory_region_dispatch_read(mr, mr_offset, &val, op, full->attrs);
|
tcg: drop global lock during TCG code execution
This finally allows TCG to benefit from the iothread introduction: Drop
the global mutex while running pure TCG CPU code. Reacquire the lock
when entering MMIO or PIO emulation, or when leaving the TCG loop.
We have to revert a few optimization for the current TCG threading
model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
kicking it in qemu_cpu_kick. We also need to disable RAM block
reordering until we have a more efficient locking mechanism at hand.
Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
These numbers demonstrate where we gain something:
20338 jan 20 0 331m 75m 6904 R 99 0.9 0:50.95 qemu-system-arm
20337 jan 20 0 331m 75m 6904 S 20 0.9 0:26.50 qemu-system-arm
The guest CPU was fully loaded, but the iothread could still run mostly
independent on a second core. Without the patch we don't get beyond
32206 jan 20 0 330m 73m 7036 R 82 0.9 1:06.00 qemu-system-arm
32204 jan 20 0 330m 73m 7036 S 21 0.9 0:17.03 qemu-system-arm
We don't benefit significantly, though, when the guest is not fully
loading a host CPU.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
[FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
[EGC: fixed iothread lock for cpu-exec IRQ handling]
Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
[PM: target-arm changes]
Acked-by: Peter Maydell <peter.maydell@linaro.org>
2017-02-23 19:29:11 +01:00
|
|
|
}
|
2022-11-18 04:59:16 +01:00
|
|
|
|
2017-09-04 16:21:55 +02:00
|
|
|
if (r != MEMTX_OK) {
|
2018-06-15 15:57:14 +02:00
|
|
|
hwaddr physaddr = mr_offset +
|
|
|
|
section->offset_within_address_space -
|
|
|
|
section->offset_within_region;
|
|
|
|
|
2019-08-23 20:36:53 +02:00
|
|
|
cpu_transaction_failed(cpu, physaddr, addr, memop_size(op), access_type,
|
2022-08-19 23:20:37 +02:00
|
|
|
mmu_idx, full->attrs, r, retaddr);
|
2017-09-04 16:21:55 +02:00
|
|
|
}
|
2016-07-09 03:51:28 +02:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2020-07-13 22:04:10 +02:00
|
|
|
/*
|
2022-08-19 23:20:37 +02:00
|
|
|
* Save a potentially trashed CPUTLBEntryFull for later lookup by plugin.
|
|
|
|
* This is read by tlb_plugin_lookup if the fulltlb entry doesn't match
|
2020-07-20 14:23:58 +02:00
|
|
|
* because of the side effect of io_writex changing memory layout.
|
2020-07-13 22:04:10 +02:00
|
|
|
*/
|
2022-08-19 23:24:34 +02:00
|
|
|
static void save_iotlb_data(CPUState *cs, MemoryRegionSection *section,
|
|
|
|
hwaddr mr_offset)
|
2020-07-13 22:04:10 +02:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_PLUGIN
|
|
|
|
SavedIOTLB *saved = &cs->saved_iotlb;
|
|
|
|
saved->section = section;
|
|
|
|
saved->mr_offset = mr_offset;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-08-19 23:20:37 +02:00
|
|
|
static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
|
2023-06-21 15:56:22 +02:00
|
|
|
int mmu_idx, uint64_t val, vaddr addr,
|
2019-08-23 20:36:53 +02:00
|
|
|
uintptr_t retaddr, MemOp op)
|
2016-07-09 03:51:28 +02:00
|
|
|
{
|
2019-03-23 00:07:18 +01:00
|
|
|
CPUState *cpu = env_cpu(env);
|
2018-06-15 15:57:14 +02:00
|
|
|
hwaddr mr_offset;
|
|
|
|
MemoryRegionSection *section;
|
|
|
|
MemoryRegion *mr;
|
2017-09-04 16:21:55 +02:00
|
|
|
MemTxResult r;
|
2016-07-09 03:51:28 +02:00
|
|
|
|
2022-08-19 23:20:37 +02:00
|
|
|
section = iotlb_to_section(cpu, full->xlat_section, full->attrs);
|
2018-06-15 15:57:14 +02:00
|
|
|
mr = section->mr;
|
2022-08-19 23:20:37 +02:00
|
|
|
mr_offset = (full->xlat_section & TARGET_PAGE_MASK) + addr;
|
2019-09-18 18:15:44 +02:00
|
|
|
if (!cpu->can_do_io) {
|
2016-07-09 03:51:28 +02:00
|
|
|
cpu_io_recompile(cpu, retaddr);
|
|
|
|
}
|
|
|
|
cpu->mem_io_pc = retaddr;
|
tcg: drop global lock during TCG code execution
This finally allows TCG to benefit from the iothread introduction: Drop
the global mutex while running pure TCG CPU code. Reacquire the lock
when entering MMIO or PIO emulation, or when leaving the TCG loop.
We have to revert a few optimization for the current TCG threading
model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
kicking it in qemu_cpu_kick. We also need to disable RAM block
reordering until we have a more efficient locking mechanism at hand.
Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
These numbers demonstrate where we gain something:
20338 jan 20 0 331m 75m 6904 R 99 0.9 0:50.95 qemu-system-arm
20337 jan 20 0 331m 75m 6904 S 20 0.9 0:26.50 qemu-system-arm
The guest CPU was fully loaded, but the iothread could still run mostly
independent on a second core. Without the patch we don't get beyond
32206 jan 20 0 330m 73m 7036 R 82 0.9 1:06.00 qemu-system-arm
32204 jan 20 0 330m 73m 7036 S 21 0.9 0:17.03 qemu-system-arm
We don't benefit significantly, though, when the guest is not fully
loading a host CPU.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
[FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
[EGC: fixed iothread lock for cpu-exec IRQ handling]
Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
[PM: target-arm changes]
Acked-by: Peter Maydell <peter.maydell@linaro.org>
2017-02-23 19:29:11 +01:00
|
|
|
|
2020-07-13 22:04:10 +02:00
|
|
|
/*
|
|
|
|
* The memory_region_dispatch may trigger a flush/resize
|
|
|
|
* so for plugins we save the iotlb_data just in case.
|
|
|
|
*/
|
2022-08-19 23:24:34 +02:00
|
|
|
save_iotlb_data(cpu, section, mr_offset);
|
2020-07-13 22:04:10 +02:00
|
|
|
|
2022-11-18 04:59:16 +01:00
|
|
|
{
|
|
|
|
QEMU_IOTHREAD_LOCK_GUARD();
|
|
|
|
r = memory_region_dispatch_write(mr, mr_offset, val, op, full->attrs);
|
tcg: drop global lock during TCG code execution
This finally allows TCG to benefit from the iothread introduction: Drop
the global mutex while running pure TCG CPU code. Reacquire the lock
when entering MMIO or PIO emulation, or when leaving the TCG loop.
We have to revert a few optimization for the current TCG threading
model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
kicking it in qemu_cpu_kick. We also need to disable RAM block
reordering until we have a more efficient locking mechanism at hand.
Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
These numbers demonstrate where we gain something:
20338 jan 20 0 331m 75m 6904 R 99 0.9 0:50.95 qemu-system-arm
20337 jan 20 0 331m 75m 6904 S 20 0.9 0:26.50 qemu-system-arm
The guest CPU was fully loaded, but the iothread could still run mostly
independent on a second core. Without the patch we don't get beyond
32206 jan 20 0 330m 73m 7036 R 82 0.9 1:06.00 qemu-system-arm
32204 jan 20 0 330m 73m 7036 S 21 0.9 0:17.03 qemu-system-arm
We don't benefit significantly, though, when the guest is not fully
loading a host CPU.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
[FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
[EGC: fixed iothread lock for cpu-exec IRQ handling]
Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
[PM: target-arm changes]
Acked-by: Peter Maydell <peter.maydell@linaro.org>
2017-02-23 19:29:11 +01:00
|
|
|
}
|
2022-11-18 04:59:16 +01:00
|
|
|
|
2017-09-04 16:21:55 +02:00
|
|
|
if (r != MEMTX_OK) {
|
2018-06-15 15:57:14 +02:00
|
|
|
hwaddr physaddr = mr_offset +
|
|
|
|
section->offset_within_address_space -
|
|
|
|
section->offset_within_region;
|
|
|
|
|
2019-08-23 20:36:53 +02:00
|
|
|
cpu_transaction_failed(cpu, physaddr, addr, memop_size(op),
|
2022-08-19 23:20:37 +02:00
|
|
|
MMU_DATA_STORE, mmu_idx, full->attrs, r,
|
2019-08-23 20:36:53 +02:00
|
|
|
retaddr);
|
2017-09-04 16:21:55 +02:00
|
|
|
}
|
2016-07-09 03:51:28 +02:00
|
|
|
}
|
|
|
|
|
2016-07-08 21:19:32 +02:00
|
|
|
/* 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,
|
2023-06-21 15:56:22 +02:00
|
|
|
MMUAccessType access_type, vaddr page)
|
2016-07-08 21:19:32 +02:00
|
|
|
{
|
|
|
|
size_t vidx;
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
|
2019-03-23 00:07:18 +01:00
|
|
|
assert_cpu_is_self(env_cpu(env));
|
2016-07-08 21:19:32 +02:00
|
|
|
for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) {
|
2019-03-22 21:52:09 +01:00
|
|
|
CPUTLBEntry *vtlb = &env_tlb(env)->d[mmu_idx].vtable[vidx];
|
2023-06-21 15:56:25 +02:00
|
|
|
uint64_t cmp = tlb_read_idx(vtlb, access_type);
|
2016-07-08 21:19:32 +02:00
|
|
|
|
|
|
|
if (cmp == page) {
|
|
|
|
/* Found entry in victim tlb, swap tlb and iotlb. */
|
2019-03-22 21:52:09 +01:00
|
|
|
CPUTLBEntry tmptlb, *tlb = &env_tlb(env)->f[mmu_idx].table[index];
|
2017-02-23 19:29:21 +01:00
|
|
|
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_lock(&env_tlb(env)->c.lock);
|
cputlb: serialize tlb updates with env->tlb_lock
Currently we rely on atomic operations for cross-CPU invalidations.
There are two cases that these atomics miss: cross-CPU invalidations
can race with either (1) vCPU threads flushing their TLB, which
happens via memset, or (2) vCPUs calling tlb_reset_dirty on their TLB,
which updates .addr_write with a regular store. This results in
undefined behaviour, since we're mixing regular and atomic ops
on concurrent accesses.
Fix it by using tlb_lock, a per-vCPU lock. All updaters of tlb_table
and the corresponding victim cache now hold the lock.
The readers that do not hold tlb_lock must use atomic reads when
reading .addr_write, since this field can be updated by other threads;
the conversion to atomic reads is done in the next patch.
Note that an alternative fix would be to expand the use of atomic ops.
However, in the case of TLB flushes this would have a huge performance
impact, since (1) TLB flushes can happen very frequently and (2) we
currently use a full memory barrier to flush each TLB entry, and a TLB
has many entries. Instead, acquiring the lock is barely slower than a
full memory barrier since it is uncontended, and with a single lock
acquisition we can flush the entire TLB.
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <20181009174557.16125-6-cota@braap.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2018-10-09 19:45:56 +02:00
|
|
|
copy_tlb_helper_locked(&tmptlb, tlb);
|
|
|
|
copy_tlb_helper_locked(tlb, vtlb);
|
|
|
|
copy_tlb_helper_locked(vtlb, &tmptlb);
|
2019-03-22 21:52:09 +01:00
|
|
|
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
2017-02-23 19:29:21 +01:00
|
|
|
|
2022-08-19 23:20:37 +02:00
|
|
|
CPUTLBEntryFull *f1 = &env_tlb(env)->d[mmu_idx].fulltlb[index];
|
|
|
|
CPUTLBEntryFull *f2 = &env_tlb(env)->d[mmu_idx].vfulltlb[vidx];
|
|
|
|
CPUTLBEntryFull tmpf;
|
|
|
|
tmpf = *f1; *f1 = *f2; *f2 = tmpf;
|
2016-07-08 21:19:32 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-09-22 03:47:59 +02:00
|
|
|
static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
|
2022-08-19 23:20:37 +02:00
|
|
|
CPUTLBEntryFull *full, uintptr_t retaddr)
|
2019-09-22 03:47:59 +02:00
|
|
|
{
|
2022-08-19 23:20:37 +02:00
|
|
|
ram_addr_t ram_addr = mem_vaddr + full->xlat_section;
|
2019-09-22 03:47:59 +02:00
|
|
|
|
|
|
|
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
|
|
|
|
|
|
|
|
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
|
2022-12-09 10:36:48 +01:00
|
|
|
tb_invalidate_phys_range_fast(ram_addr, size, retaddr);
|
2019-09-22 03:47:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set both VGA and migration bits for simplicity and to remove
|
|
|
|
* the notdirty callback faster.
|
|
|
|
*/
|
|
|
|
cpu_physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE);
|
|
|
|
|
|
|
|
/* We remove the notdirty callback only if the code has been flushed. */
|
|
|
|
if (!cpu_physical_memory_is_clean(ram_addr)) {
|
|
|
|
trace_memory_notdirty_set_dirty(mem_vaddr);
|
|
|
|
tlb_set_dirty(cpu, mem_vaddr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
static int probe_access_internal(CPUArchState *env, target_ulong addr,
|
|
|
|
int fault_size, MMUAccessType access_type,
|
|
|
|
int mmu_idx, bool nonfault,
|
2022-08-20 00:49:41 +02:00
|
|
|
void **phost, CPUTLBEntryFull **pfull,
|
|
|
|
uintptr_t retaddr)
|
2016-07-09 03:22:26 +02:00
|
|
|
{
|
2018-10-09 19:51:25 +02:00
|
|
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
|
|
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
2023-06-21 15:56:25 +02:00
|
|
|
uint64_t tlb_addr = tlb_read_idx(entry, access_type);
|
2023-05-05 22:55:01 +02:00
|
|
|
target_ulong page_addr = addr & TARGET_PAGE_MASK;
|
|
|
|
int flags = TLB_FLAGS_MASK;
|
2019-08-30 12:09:59 +02:00
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
if (!tlb_hit_page(tlb_addr, page_addr)) {
|
2023-05-05 22:55:01 +02:00
|
|
|
if (!victim_tlb_hit(env, mmu_idx, index, access_type, page_addr)) {
|
2020-05-08 17:43:45 +02:00
|
|
|
CPUState *cs = env_cpu(env);
|
|
|
|
|
2022-09-23 10:48:01 +02:00
|
|
|
if (!cs->cc->tcg_ops->tlb_fill(cs, addr, fault_size, access_type,
|
|
|
|
mmu_idx, nonfault, retaddr)) {
|
2020-05-08 17:43:45 +02:00
|
|
|
/* Non-faulting page table read failed. */
|
|
|
|
*phost = NULL;
|
2022-08-20 00:49:41 +02:00
|
|
|
*pfull = NULL;
|
2020-05-08 17:43:45 +02:00
|
|
|
return TLB_INVALID_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TLB resize via tlb_fill may have moved the entry. */
|
2022-08-20 00:49:41 +02:00
|
|
|
index = tlb_index(env, mmu_idx, addr);
|
2019-08-23 12:07:41 +02:00
|
|
|
entry = tlb_entry(env, mmu_idx, addr);
|
2022-08-20 00:28:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* With PAGE_WRITE_INV, we set TLB_INVALID_MASK immediately,
|
|
|
|
* to force the next access through tlb_fill. We've just
|
|
|
|
* called tlb_fill, so we know that this entry *is* valid.
|
|
|
|
*/
|
|
|
|
flags &= ~TLB_INVALID_MASK;
|
2016-07-09 03:22:26 +02:00
|
|
|
}
|
2023-05-05 22:55:01 +02:00
|
|
|
tlb_addr = tlb_read_idx(entry, access_type);
|
2019-08-23 12:07:41 +02:00
|
|
|
}
|
2022-08-20 00:28:05 +02:00
|
|
|
flags &= tlb_addr;
|
2019-08-23 12:07:41 +02:00
|
|
|
|
2022-08-20 00:49:41 +02:00
|
|
|
*pfull = &env_tlb(env)->d[mmu_idx].fulltlb[index];
|
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
|
|
|
|
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) {
|
|
|
|
*phost = NULL;
|
|
|
|
return TLB_MMIO;
|
2019-08-30 12:09:58 +02:00
|
|
|
}
|
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
/* Everything else is RAM. */
|
|
|
|
*phost = (void *)((uintptr_t)addr + entry->addend);
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2023-02-24 01:44:14 +01:00
|
|
|
int probe_access_full(CPUArchState *env, target_ulong addr, int size,
|
2022-08-20 00:49:41 +02:00
|
|
|
MMUAccessType access_type, int mmu_idx,
|
|
|
|
bool nonfault, void **phost, CPUTLBEntryFull **pfull,
|
|
|
|
uintptr_t retaddr)
|
2020-05-08 17:43:45 +02:00
|
|
|
{
|
2023-02-24 01:44:14 +01:00
|
|
|
int flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
|
2022-08-20 00:49:41 +02:00
|
|
|
nonfault, phost, pfull, retaddr);
|
2020-05-08 17:43:45 +02:00
|
|
|
|
|
|
|
/* Handle clean RAM pages. */
|
|
|
|
if (unlikely(flags & TLB_NOTDIRTY)) {
|
2022-08-20 00:49:41 +02:00
|
|
|
notdirty_write(env_cpu(env), addr, 1, *pfull, retaddr);
|
2020-05-08 17:43:45 +02:00
|
|
|
flags &= ~TLB_NOTDIRTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2023-02-24 00:44:24 +01:00
|
|
|
int probe_access_flags(CPUArchState *env, target_ulong addr, int size,
|
2022-08-20 00:49:41 +02:00
|
|
|
MMUAccessType access_type, int mmu_idx,
|
|
|
|
bool nonfault, void **phost, uintptr_t retaddr)
|
|
|
|
{
|
|
|
|
CPUTLBEntryFull *full;
|
2023-02-24 00:44:24 +01:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
|
|
|
|
|
|
|
flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
|
|
|
|
nonfault, phost, &full, retaddr);
|
2022-08-20 00:49:41 +02:00
|
|
|
|
2023-02-24 00:44:24 +01:00
|
|
|
/* Handle clean RAM pages. */
|
|
|
|
if (unlikely(flags & TLB_NOTDIRTY)) {
|
|
|
|
notdirty_write(env_cpu(env), addr, 1, full, retaddr);
|
|
|
|
flags &= ~TLB_NOTDIRTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
2022-08-20 00:49:41 +02:00
|
|
|
}
|
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
|
|
|
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
|
|
|
{
|
2022-08-20 00:49:41 +02:00
|
|
|
CPUTLBEntryFull *full;
|
2020-05-08 17:43:45 +02:00
|
|
|
void *host;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
|
|
|
|
|
|
|
flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
|
2022-08-20 00:49:41 +02:00
|
|
|
false, &host, &full, retaddr);
|
2020-05-08 17:43:45 +02:00
|
|
|
|
|
|
|
/* Per the interface, size == 0 merely faults the access. */
|
|
|
|
if (size == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(flags & (TLB_NOTDIRTY | TLB_WATCHPOINT))) {
|
2019-09-22 04:28:48 +02:00
|
|
|
/* Handle watchpoints. */
|
2020-05-08 17:43:45 +02:00
|
|
|
if (flags & TLB_WATCHPOINT) {
|
|
|
|
int wp_access = (access_type == MMU_DATA_STORE
|
|
|
|
? BP_MEM_WRITE : BP_MEM_READ);
|
2019-09-22 04:28:48 +02:00
|
|
|
cpu_check_watchpoint(env_cpu(env), addr, size,
|
2022-08-19 23:20:37 +02:00
|
|
|
full->attrs, wp_access, retaddr);
|
2019-09-22 04:28:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle clean RAM pages. */
|
2020-05-08 17:43:45 +02:00
|
|
|
if (flags & TLB_NOTDIRTY) {
|
2022-08-19 23:20:37 +02:00
|
|
|
notdirty_write(env_cpu(env), addr, 1, full, retaddr);
|
2019-09-22 04:28:48 +02:00
|
|
|
}
|
2019-08-30 12:09:58 +02:00
|
|
|
}
|
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
return host;
|
2016-07-09 03:22:26 +02:00
|
|
|
}
|
|
|
|
|
2019-04-03 05:16:56 +02:00
|
|
|
void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
|
|
|
|
MMUAccessType access_type, int mmu_idx)
|
|
|
|
{
|
2022-08-20 00:49:41 +02:00
|
|
|
CPUTLBEntryFull *full;
|
2020-05-08 17:43:45 +02:00
|
|
|
void *host;
|
|
|
|
int flags;
|
2019-04-03 05:16:56 +02:00
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
flags = probe_access_internal(env, addr, 0, access_type,
|
2022-08-20 00:49:41 +02:00
|
|
|
mmu_idx, true, &host, &full, 0);
|
2019-04-03 05:16:56 +02:00
|
|
|
|
2020-05-08 17:43:45 +02:00
|
|
|
/* No combination of flags are expected by the caller. */
|
|
|
|
return flags ? NULL : host;
|
2019-04-03 05:16:56 +02:00
|
|
|
}
|
|
|
|
|
2022-08-10 23:13:30 +02:00
|
|
|
/*
|
|
|
|
* Return a ram_addr_t for the virtual address for execution.
|
|
|
|
*
|
|
|
|
* Return -1 if we can't translate and execute from an entire page
|
|
|
|
* of RAM. This will force us to execute by loading and translating
|
|
|
|
* one insn at a time, without caching.
|
|
|
|
*
|
|
|
|
* 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,
|
|
|
|
void **hostp)
|
|
|
|
{
|
2022-08-20 00:49:41 +02:00
|
|
|
CPUTLBEntryFull *full;
|
2022-08-10 23:13:30 +02:00
|
|
|
void *p;
|
|
|
|
|
|
|
|
(void)probe_access_internal(env, addr, 1, MMU_INST_FETCH,
|
2022-08-20 00:49:41 +02:00
|
|
|
cpu_mmu_index(env, true), false, &p, &full, 0);
|
2022-08-10 23:13:30 +02:00
|
|
|
if (p == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
2023-04-22 15:03:27 +02:00
|
|
|
|
|
|
|
if (full->lg_page_size < TARGET_PAGE_BITS) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-08-10 23:13:30 +02:00
|
|
|
if (hostp) {
|
|
|
|
*hostp = p;
|
|
|
|
}
|
|
|
|
return qemu_ram_addr_from_host_nofail(p);
|
|
|
|
}
|
|
|
|
|
2022-10-29 07:01:04 +02:00
|
|
|
/* Load/store with atomicity primitives. */
|
|
|
|
#include "ldst_atomicity.c.inc"
|
|
|
|
|
2019-06-19 21:20:08 +02:00
|
|
|
#ifdef CONFIG_PLUGIN
|
|
|
|
/*
|
|
|
|
* Perform a TLB lookup and populate the qemu_plugin_hwaddr structure.
|
|
|
|
* This should be a hot path as we will have just looked this path up
|
|
|
|
* in the softmmu lookup code (or helper). We don't handle re-fills or
|
|
|
|
* checking the victim table. This is purely informational.
|
|
|
|
*
|
2020-07-13 22:04:10 +02:00
|
|
|
* This almost never fails as the memory access being instrumented
|
|
|
|
* should have just filled the TLB. The one corner case is io_writex
|
|
|
|
* which can cause TLB flushes and potential resizing of the TLBs
|
2020-07-20 14:23:58 +02:00
|
|
|
* losing the information we need. In those cases we need to recover
|
2022-08-19 23:20:37 +02:00
|
|
|
* data from a copy of the CPUTLBEntryFull. As long as this always occurs
|
2020-07-20 14:23:58 +02:00
|
|
|
* from the same thread (which a mem callback will be) this is safe.
|
2019-06-19 21:20:08 +02:00
|
|
|
*/
|
|
|
|
|
2023-06-21 15:56:22 +02:00
|
|
|
bool tlb_plugin_lookup(CPUState *cpu, vaddr addr, int mmu_idx,
|
2019-06-19 21:20:08 +02:00
|
|
|
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);
|
2023-06-21 15:56:25 +02:00
|
|
|
uint64_t tlb_addr = is_store ? tlb_addr_write(tlbe) : tlbe->addr_read;
|
2019-06-19 21:20:08 +02:00
|
|
|
|
|
|
|
if (likely(tlb_hit(tlb_addr, addr))) {
|
|
|
|
/* We must have an iotlb entry for MMIO */
|
|
|
|
if (tlb_addr & TLB_MMIO) {
|
2022-08-19 23:20:37 +02:00
|
|
|
CPUTLBEntryFull *full;
|
|
|
|
full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
|
2019-06-19 21:20:08 +02:00
|
|
|
data->is_io = true;
|
2022-08-19 23:20:37 +02:00
|
|
|
data->v.io.section =
|
|
|
|
iotlb_to_section(cpu, full->xlat_section, full->attrs);
|
|
|
|
data->v.io.offset = (full->xlat_section & TARGET_PAGE_MASK) + addr;
|
2019-06-19 21:20:08 +02:00
|
|
|
} else {
|
|
|
|
data->is_io = false;
|
2021-07-09 16:29:52 +02:00
|
|
|
data->v.ram.hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
|
2019-06-19 21:20:08 +02:00
|
|
|
}
|
|
|
|
return true;
|
2020-07-13 22:04:10 +02:00
|
|
|
} else {
|
|
|
|
SavedIOTLB *saved = &cpu->saved_iotlb;
|
|
|
|
data->is_io = true;
|
|
|
|
data->v.io.section = saved->section;
|
|
|
|
data->v.io.offset = saved->mr_offset;
|
|
|
|
return true;
|
2019-06-19 21:20:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/*
|
|
|
|
* Probe for a load/store operation.
|
|
|
|
* Return the host address and into @flags.
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct MMULookupPageData {
|
|
|
|
CPUTLBEntryFull *full;
|
|
|
|
void *haddr;
|
2023-06-21 15:56:26 +02:00
|
|
|
vaddr addr;
|
2022-10-28 23:40:51 +02:00
|
|
|
int flags;
|
|
|
|
int size;
|
|
|
|
} MMULookupPageData;
|
|
|
|
|
|
|
|
typedef struct MMULookupLocals {
|
|
|
|
MMULookupPageData page[2];
|
|
|
|
MemOp memop;
|
|
|
|
int mmu_idx;
|
|
|
|
} MMULookupLocals;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mmu_lookup1: translate one page
|
|
|
|
* @env: cpu context
|
|
|
|
* @data: lookup parameters
|
|
|
|
* @mmu_idx: virtual address context
|
|
|
|
* @access_type: load/store/code
|
|
|
|
* @ra: return address into tcg generated code, or 0
|
|
|
|
*
|
|
|
|
* Resolve the translation for the one page at @data.addr, filling in
|
|
|
|
* the rest of @data with the results. If the translation fails,
|
|
|
|
* tlb_fill will longjmp out. Return true if the softmmu tlb for
|
|
|
|
* @mmu_idx may have resized.
|
|
|
|
*/
|
|
|
|
static bool mmu_lookup1(CPUArchState *env, MMULookupPageData *data,
|
|
|
|
int mmu_idx, MMUAccessType access_type, uintptr_t ra)
|
|
|
|
{
|
2023-06-21 15:56:26 +02:00
|
|
|
vaddr addr = data->addr;
|
2022-10-28 23:40:51 +02:00
|
|
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
|
|
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
2023-06-21 15:56:25 +02:00
|
|
|
uint64_t tlb_addr = tlb_read_idx(entry, access_type);
|
2022-10-28 23:40:51 +02:00
|
|
|
bool maybe_resized = false;
|
|
|
|
|
|
|
|
/* If the TLB entry is for a different page, reload and try again. */
|
|
|
|
if (!tlb_hit(tlb_addr, addr)) {
|
|
|
|
if (!victim_tlb_hit(env, mmu_idx, index, access_type,
|
|
|
|
addr & TARGET_PAGE_MASK)) {
|
|
|
|
tlb_fill(env_cpu(env), addr, data->size, access_type, mmu_idx, ra);
|
|
|
|
maybe_resized = true;
|
|
|
|
index = tlb_index(env, mmu_idx, addr);
|
|
|
|
entry = tlb_entry(env, mmu_idx, 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];
|
|
|
|
/* Compute haddr speculatively; depending on flags it might be invalid. */
|
|
|
|
data->haddr = (void *)((uintptr_t)addr + entry->addend);
|
|
|
|
|
|
|
|
return maybe_resized;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mmu_watch_or_dirty
|
|
|
|
* @env: cpu context
|
|
|
|
* @data: lookup parameters
|
|
|
|
* @access_type: load/store/code
|
|
|
|
* @ra: return address into tcg generated code, or 0
|
|
|
|
*
|
|
|
|
* Trigger watchpoints for @data.addr:@data.size;
|
|
|
|
* record writes to protected clean pages.
|
|
|
|
*/
|
|
|
|
static void mmu_watch_or_dirty(CPUArchState *env, MMULookupPageData *data,
|
|
|
|
MMUAccessType access_type, uintptr_t ra)
|
|
|
|
{
|
|
|
|
CPUTLBEntryFull *full = data->full;
|
2023-06-21 15:56:26 +02:00
|
|
|
vaddr addr = data->addr;
|
2022-10-28 23:40:51 +02:00
|
|
|
int flags = data->flags;
|
|
|
|
int size = data->size;
|
|
|
|
|
|
|
|
/* On watchpoint hit, this will longjmp out. */
|
|
|
|
if (flags & TLB_WATCHPOINT) {
|
|
|
|
int wp = access_type == MMU_DATA_STORE ? BP_MEM_WRITE : BP_MEM_READ;
|
|
|
|
cpu_check_watchpoint(env_cpu(env), addr, size, full->attrs, wp, ra);
|
|
|
|
flags &= ~TLB_WATCHPOINT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note that notdirty is only set for writes. */
|
|
|
|
if (flags & TLB_NOTDIRTY) {
|
|
|
|
notdirty_write(env_cpu(env), addr, size, full, ra);
|
|
|
|
flags &= ~TLB_NOTDIRTY;
|
|
|
|
}
|
|
|
|
data->flags = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mmu_lookup: translate page(s)
|
|
|
|
* @env: cpu context
|
|
|
|
* @addr: virtual address
|
|
|
|
* @oi: combined mmu_idx and MemOp
|
|
|
|
* @ra: return address into tcg generated code, or 0
|
|
|
|
* @access_type: load/store/code
|
|
|
|
* @l: output result
|
|
|
|
*
|
|
|
|
* Resolve the translation for the page(s) beginning at @addr, for MemOp.size
|
|
|
|
* bytes. Return true if the lookup crosses a page boundary.
|
|
|
|
*/
|
2023-06-21 15:56:26 +02:00
|
|
|
static bool mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi,
|
2022-10-28 23:40:51 +02:00
|
|
|
uintptr_t ra, MMUAccessType type, MMULookupLocals *l)
|
|
|
|
{
|
|
|
|
unsigned a_bits;
|
|
|
|
bool crosspage;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
l->memop = get_memop(oi);
|
|
|
|
l->mmu_idx = get_mmuidx(oi);
|
|
|
|
|
|
|
|
tcg_debug_assert(l->mmu_idx < NB_MMU_MODES);
|
|
|
|
|
|
|
|
/* Handle CPU specific unaligned behaviour */
|
|
|
|
a_bits = get_alignment_bits(l->memop);
|
|
|
|
if (addr & ((1 << a_bits) - 1)) {
|
|
|
|
cpu_unaligned_access(env_cpu(env), addr, type, l->mmu_idx, ra);
|
|
|
|
}
|
|
|
|
|
|
|
|
l->page[0].addr = addr;
|
|
|
|
l->page[0].size = memop_size(l->memop);
|
|
|
|
l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK;
|
|
|
|
l->page[1].size = 0;
|
|
|
|
crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK;
|
|
|
|
|
|
|
|
if (likely(!crosspage)) {
|
|
|
|
mmu_lookup1(env, &l->page[0], l->mmu_idx, type, ra);
|
|
|
|
|
|
|
|
flags = l->page[0].flags;
|
|
|
|
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
|
|
|
|
mmu_watch_or_dirty(env, &l->page[0], type, ra);
|
|
|
|
}
|
|
|
|
if (unlikely(flags & TLB_BSWAP)) {
|
|
|
|
l->memop ^= MO_BSWAP;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Finish compute of page crossing. */
|
|
|
|
int size0 = l->page[1].addr - addr;
|
|
|
|
l->page[1].size = l->page[0].size - size0;
|
|
|
|
l->page[0].size = size0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup both pages, recognizing exceptions from either. If the
|
|
|
|
* second lookup potentially resized, refresh first CPUTLBEntryFull.
|
|
|
|
*/
|
|
|
|
mmu_lookup1(env, &l->page[0], l->mmu_idx, type, ra);
|
|
|
|
if (mmu_lookup1(env, &l->page[1], l->mmu_idx, type, ra)) {
|
|
|
|
uintptr_t index = tlb_index(env, l->mmu_idx, addr);
|
|
|
|
l->page[0].full = &env_tlb(env)->d[l->mmu_idx].fulltlb[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = l->page[0].flags | l->page[1].flags;
|
|
|
|
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
|
|
|
|
mmu_watch_or_dirty(env, &l->page[0], type, ra);
|
|
|
|
mmu_watch_or_dirty(env, &l->page[1], type, ra);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since target/sparc is the only user of TLB_BSWAP, and all
|
|
|
|
* Sparc accesses are aligned, any treatment across two pages
|
|
|
|
* would be arbitrary. Refuse it until there's a use.
|
|
|
|
*/
|
|
|
|
tcg_debug_assert((flags & TLB_BSWAP) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return crosspage;
|
|
|
|
}
|
|
|
|
|
2021-06-13 02:21:06 +02:00
|
|
|
/*
|
|
|
|
* Probe for an atomic operation. Do not allow unaligned operations,
|
|
|
|
* or io operations to proceed. Return the host address.
|
|
|
|
*/
|
2016-06-28 20:37:27 +02:00
|
|
|
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
2023-05-20 02:54:18 +02:00
|
|
|
MemOpIdx oi, int size, uintptr_t retaddr)
|
2016-06-28 20:37:27 +02:00
|
|
|
{
|
2022-04-01 19:08:13 +02:00
|
|
|
uintptr_t mmu_idx = get_mmuidx(oi);
|
2019-08-23 20:10:58 +02:00
|
|
|
MemOp mop = get_memop(oi);
|
2016-06-28 20:37:27 +02:00
|
|
|
int a_bits = get_alignment_bits(mop);
|
2021-06-13 02:21:06 +02:00
|
|
|
uintptr_t index;
|
|
|
|
CPUTLBEntry *tlbe;
|
|
|
|
target_ulong tlb_addr;
|
2017-11-20 19:08:28 +01:00
|
|
|
void *hostaddr;
|
2023-02-23 09:41:01 +01:00
|
|
|
CPUTLBEntryFull *full;
|
2016-06-28 20:37:27 +02:00
|
|
|
|
2022-04-01 19:08:13 +02:00
|
|
|
tcg_debug_assert(mmu_idx < NB_MMU_MODES);
|
|
|
|
|
2016-06-28 20:37:27 +02:00
|
|
|
/* Adjust the given return address. */
|
|
|
|
retaddr -= GETPC_ADJ;
|
|
|
|
|
|
|
|
/* Enforce guest required alignment. */
|
|
|
|
if (unlikely(a_bits > 0 && (addr & ((1 << a_bits) - 1)))) {
|
|
|
|
/* ??? Maybe indicate atomic op to cpu_unaligned_access */
|
2019-03-23 00:07:18 +01:00
|
|
|
cpu_unaligned_access(env_cpu(env), addr, MMU_DATA_STORE,
|
2016-06-28 20:37:27 +02:00
|
|
|
mmu_idx, retaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enforce qemu required alignment. */
|
2021-06-13 02:21:06 +02:00
|
|
|
if (unlikely(addr & (size - 1))) {
|
2016-06-28 20:37:27 +02:00
|
|
|
/* We get here if guest alignment was not requested,
|
|
|
|
or was not enforced by cpu_unaligned_access above.
|
|
|
|
We might widen the access and emulate, but for now
|
|
|
|
mark an exception and exit the cpu loop. */
|
|
|
|
goto stop_the_world;
|
|
|
|
}
|
|
|
|
|
2021-06-13 02:21:06 +02:00
|
|
|
index = tlb_index(env, mmu_idx, addr);
|
|
|
|
tlbe = tlb_entry(env, mmu_idx, addr);
|
|
|
|
|
2016-06-28 20:37:27 +02:00
|
|
|
/* Check TLB entry and enforce page permissions. */
|
2023-05-20 02:54:18 +02:00
|
|
|
tlb_addr = tlb_addr_write(tlbe);
|
|
|
|
if (!tlb_hit(tlb_addr, addr)) {
|
|
|
|
if (!victim_tlb_hit(env, mmu_idx, index, MMU_DATA_STORE,
|
|
|
|
addr & TARGET_PAGE_MASK)) {
|
|
|
|
tlb_fill(env_cpu(env), addr, size,
|
|
|
|
MMU_DATA_STORE, mmu_idx, retaddr);
|
|
|
|
index = tlb_index(env, mmu_idx, addr);
|
|
|
|
tlbe = tlb_entry(env, mmu_idx, addr);
|
2021-06-13 02:21:06 +02:00
|
|
|
}
|
2023-05-20 02:54:18 +02:00
|
|
|
tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK;
|
|
|
|
}
|
2021-06-13 02:21:06 +02:00
|
|
|
|
2023-05-20 02:54:18 +02:00
|
|
|
/*
|
|
|
|
* Let the guest notice RMW on a write-only page.
|
|
|
|
* We have just verified that the page is writable.
|
|
|
|
* Subpage lookups may have left TLB_INVALID_MASK set,
|
|
|
|
* but addr_read will only be -1 if PAGE_READ was unset.
|
|
|
|
*/
|
|
|
|
if (unlikely(tlbe->addr_read == -1)) {
|
|
|
|
tlb_fill(env_cpu(env), addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
|
|
|
|
/*
|
|
|
|
* Since we don't support reads and writes to different
|
|
|
|
* addresses, and we do have the proper page loaded for
|
|
|
|
* write, this shouldn't ever return. But just in case,
|
|
|
|
* handle via stop-the-world.
|
|
|
|
*/
|
|
|
|
goto stop_the_world;
|
2016-06-28 20:37:27 +02:00
|
|
|
}
|
2023-05-20 02:54:18 +02:00
|
|
|
/* Collect TLB_WATCHPOINT for read. */
|
|
|
|
tlb_addr |= tlbe->addr_read;
|
2016-06-28 20:37:27 +02:00
|
|
|
|
2018-06-26 18:50:41 +02:00
|
|
|
/* Notice an IO access or a needs-MMU-lookup access */
|
2023-02-23 10:05:01 +01:00
|
|
|
if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) {
|
2016-06-28 20:37:27 +02:00
|
|
|
/* There's really nothing that can be done to
|
|
|
|
support this apart from stop-the-world. */
|
|
|
|
goto stop_the_world;
|
|
|
|
}
|
|
|
|
|
2017-11-20 19:08:28 +01:00
|
|
|
hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
|
2023-02-23 09:41:01 +01:00
|
|
|
full = &env_tlb(env)->d[mmu_idx].fulltlb[index];
|
2017-11-20 19:08:28 +01:00
|
|
|
|
|
|
|
if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
|
2023-02-23 09:41:01 +01:00
|
|
|
notdirty_write(env_cpu(env), addr, size, full, retaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
|
2023-05-20 02:54:18 +02:00
|
|
|
cpu_check_watchpoint(env_cpu(env), addr, size, full->attrs,
|
|
|
|
BP_MEM_READ | BP_MEM_WRITE, retaddr);
|
2017-11-20 19:08:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return hostaddr;
|
2016-06-28 20:37:27 +02:00
|
|
|
|
|
|
|
stop_the_world:
|
2019-03-23 00:07:18 +01:00
|
|
|
cpu_loop_exit_atomic(env_cpu(env), retaddr);
|
2016-06-28 20:37:27 +02:00
|
|
|
}
|
|
|
|
|
2019-02-15 15:31:13 +01:00
|
|
|
/*
|
|
|
|
* Load Helpers
|
|
|
|
*
|
|
|
|
* We support two different access types. SOFTMMU_CODE_ACCESS is
|
|
|
|
* specifically for reading instructions from system memory. It is
|
|
|
|
* called by the translation loop and in some helpers where the code
|
|
|
|
* is disassembled. It shouldn't be called directly by guest code.
|
2022-10-29 07:01:04 +02:00
|
|
|
*
|
2022-10-28 23:40:51 +02:00
|
|
|
* For the benefit of TCG generated code, we want to avoid the
|
|
|
|
* complication of ABI-specific return type promotion and always
|
|
|
|
* return a value extended to the register size of the host. This is
|
|
|
|
* tcg_target_long, except in the case of a 32-bit host and 64-bit
|
|
|
|
* data, and for that we always have uint64_t.
|
|
|
|
*
|
|
|
|
* We don't bother with this widened value for SOFTMMU_CODE_ACCESS.
|
|
|
|
*/
|
2022-04-01 19:08:13 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/**
|
|
|
|
* do_ld_mmio_beN:
|
|
|
|
* @env: cpu context
|
|
|
|
* @p: translation parameters
|
|
|
|
* @ret_be: accumulated data
|
|
|
|
* @mmu_idx: virtual address context
|
|
|
|
* @ra: return address into tcg generated code, or 0
|
|
|
|
*
|
|
|
|
* Load @p->size bytes from @p->addr, which is memory-mapped i/o.
|
|
|
|
* The bytes are concatenated in big-endian order with @ret_be.
|
|
|
|
*/
|
|
|
|
static uint64_t do_ld_mmio_beN(CPUArchState *env, MMULookupPageData *p,
|
|
|
|
uint64_t ret_be, int mmu_idx,
|
|
|
|
MMUAccessType type, uintptr_t ra)
|
|
|
|
{
|
|
|
|
CPUTLBEntryFull *full = p->full;
|
2023-06-21 15:56:26 +02:00
|
|
|
vaddr addr = p->addr;
|
2022-10-28 23:40:51 +02:00
|
|
|
int i, size = p->size;
|
2019-02-15 15:31:13 +01:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
QEMU_IOTHREAD_LOCK_GUARD();
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
uint8_t x = io_readx(env, full, mmu_idx, addr + i, ra, type, MO_UB);
|
|
|
|
ret_be = (ret_be << 8) | x;
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2022-10-28 23:40:51 +02:00
|
|
|
return ret_be;
|
|
|
|
}
|
2014-03-28 17:55:24 +01:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/**
|
|
|
|
* do_ld_bytes_beN
|
|
|
|
* @p: translation parameters
|
|
|
|
* @ret_be: accumulated data
|
|
|
|
*
|
|
|
|
* Load @p->size bytes from @p->haddr, which is RAM.
|
|
|
|
* The bytes to concatenated in big-endian order with @ret_be.
|
|
|
|
*/
|
|
|
|
static uint64_t do_ld_bytes_beN(MMULookupPageData *p, uint64_t ret_be)
|
|
|
|
{
|
|
|
|
uint8_t *haddr = p->haddr;
|
|
|
|
int i, size = p->size;
|
2022-04-01 19:08:13 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
ret_be = (ret_be << 8) | haddr[i];
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2022-10-28 23:40:51 +02:00
|
|
|
return ret_be;
|
|
|
|
}
|
2019-02-15 15:31:13 +01:00
|
|
|
|
2022-10-29 07:01:04 +02:00
|
|
|
/**
|
|
|
|
* do_ld_parts_beN
|
|
|
|
* @p: translation parameters
|
|
|
|
* @ret_be: accumulated data
|
|
|
|
*
|
|
|
|
* As do_ld_bytes_beN, but atomically on each aligned part.
|
|
|
|
*/
|
|
|
|
static uint64_t do_ld_parts_beN(MMULookupPageData *p, uint64_t ret_be)
|
|
|
|
{
|
|
|
|
void *haddr = p->haddr;
|
|
|
|
int size = p->size;
|
|
|
|
|
|
|
|
do {
|
|
|
|
uint64_t x;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find minimum of alignment and size.
|
|
|
|
* This is slightly stronger than required by MO_ATOM_SUBALIGN, which
|
|
|
|
* would have only checked the low bits of addr|size once at the start,
|
|
|
|
* but is just as easy.
|
|
|
|
*/
|
|
|
|
switch (((uintptr_t)haddr | size) & 7) {
|
|
|
|
case 4:
|
|
|
|
x = cpu_to_be32(load_atomic4(haddr));
|
|
|
|
ret_be = (ret_be << 32) | x;
|
|
|
|
n = 4;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 6:
|
|
|
|
x = cpu_to_be16(load_atomic2(haddr));
|
|
|
|
ret_be = (ret_be << 16) | x;
|
|
|
|
n = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
x = *(uint8_t *)haddr;
|
|
|
|
ret_be = (ret_be << 8) | x;
|
|
|
|
n = 1;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
haddr += n;
|
|
|
|
size -= n;
|
|
|
|
} while (size != 0);
|
|
|
|
return ret_be;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_ld_parts_be4
|
|
|
|
* @p: translation parameters
|
|
|
|
* @ret_be: accumulated data
|
|
|
|
*
|
|
|
|
* As do_ld_bytes_beN, but with one atomic load.
|
|
|
|
* Four aligned bytes are guaranteed to cover the load.
|
|
|
|
*/
|
|
|
|
static uint64_t do_ld_whole_be4(MMULookupPageData *p, uint64_t ret_be)
|
|
|
|
{
|
|
|
|
int o = p->addr & 3;
|
|
|
|
uint32_t x = load_atomic4(p->haddr - o);
|
|
|
|
|
|
|
|
x = cpu_to_be32(x);
|
|
|
|
x <<= o * 8;
|
|
|
|
x >>= (4 - p->size) * 8;
|
|
|
|
return (ret_be << (p->size * 8)) | x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_ld_parts_be8
|
|
|
|
* @p: translation parameters
|
|
|
|
* @ret_be: accumulated data
|
|
|
|
*
|
|
|
|
* As do_ld_bytes_beN, but with one atomic load.
|
|
|
|
* Eight aligned bytes are guaranteed to cover the load.
|
|
|
|
*/
|
|
|
|
static uint64_t do_ld_whole_be8(CPUArchState *env, uintptr_t ra,
|
|
|
|
MMULookupPageData *p, uint64_t ret_be)
|
|
|
|
{
|
|
|
|
int o = p->addr & 7;
|
|
|
|
uint64_t x = load_atomic8_or_exit(env, ra, p->haddr - o);
|
|
|
|
|
|
|
|
x = cpu_to_be64(x);
|
|
|
|
x <<= o * 8;
|
|
|
|
x >>= (8 - p->size) * 8;
|
|
|
|
return (ret_be << (p->size * 8)) | x;
|
|
|
|
}
|
|
|
|
|
2023-02-15 09:16:17 +01:00
|
|
|
/**
|
|
|
|
* do_ld_parts_be16
|
|
|
|
* @p: translation parameters
|
|
|
|
* @ret_be: accumulated data
|
|
|
|
*
|
|
|
|
* As do_ld_bytes_beN, but with one atomic load.
|
|
|
|
* 16 aligned bytes are guaranteed to cover the load.
|
|
|
|
*/
|
|
|
|
static Int128 do_ld_whole_be16(CPUArchState *env, uintptr_t ra,
|
|
|
|
MMULookupPageData *p, uint64_t ret_be)
|
|
|
|
{
|
|
|
|
int o = p->addr & 15;
|
|
|
|
Int128 x, y = load_atomic16_or_exit(env, ra, p->haddr - o);
|
|
|
|
int size = p->size;
|
|
|
|
|
|
|
|
if (!HOST_BIG_ENDIAN) {
|
|
|
|
y = bswap128(y);
|
|
|
|
}
|
|
|
|
y = int128_lshift(y, o * 8);
|
|
|
|
y = int128_urshift(y, (16 - size) * 8);
|
|
|
|
x = int128_make64(ret_be);
|
|
|
|
x = int128_lshift(x, size * 8);
|
|
|
|
return int128_or(x, y);
|
|
|
|
}
|
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/*
|
|
|
|
* Wrapper for the above.
|
|
|
|
*/
|
|
|
|
static uint64_t do_ld_beN(CPUArchState *env, MMULookupPageData *p,
|
2022-10-29 07:01:04 +02:00
|
|
|
uint64_t ret_be, int mmu_idx, MMUAccessType type,
|
|
|
|
MemOp mop, uintptr_t ra)
|
2022-10-28 23:40:51 +02:00
|
|
|
{
|
2022-10-29 07:01:04 +02:00
|
|
|
MemOp atom;
|
|
|
|
unsigned tmp, half_size;
|
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
return do_ld_mmio_beN(env, p, ret_be, mmu_idx, type, ra);
|
2022-10-29 07:01:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is a given that we cross a page and therefore there is no
|
|
|
|
* atomicity for the load as a whole, but subobjects may need attention.
|
|
|
|
*/
|
|
|
|
atom = mop & MO_ATOM_MASK;
|
|
|
|
switch (atom) {
|
|
|
|
case MO_ATOM_SUBALIGN:
|
|
|
|
return do_ld_parts_beN(p, ret_be);
|
|
|
|
|
|
|
|
case MO_ATOM_IFALIGN_PAIR:
|
|
|
|
case MO_ATOM_WITHIN16_PAIR:
|
|
|
|
tmp = mop & MO_SIZE;
|
|
|
|
tmp = tmp ? tmp - 1 : 0;
|
|
|
|
half_size = 1 << tmp;
|
|
|
|
if (atom == MO_ATOM_IFALIGN_PAIR
|
|
|
|
? p->size == half_size
|
|
|
|
: p->size >= half_size) {
|
|
|
|
if (!HAVE_al8_fast && p->size < 4) {
|
|
|
|
return do_ld_whole_be4(p, ret_be);
|
|
|
|
} else {
|
|
|
|
return do_ld_whole_be8(env, ra, p, ret_be);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case MO_ATOM_IFALIGN:
|
|
|
|
case MO_ATOM_WITHIN16:
|
|
|
|
case MO_ATOM_NONE:
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld_bytes_beN(p, ret_be);
|
2022-10-29 07:01:04 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
2022-10-28 23:40:51 +02:00
|
|
|
}
|
|
|
|
}
|
2019-08-24 18:51:09 +02:00
|
|
|
|
2023-02-15 09:16:17 +01:00
|
|
|
/*
|
|
|
|
* Wrapper for the above, for 8 < size < 16.
|
|
|
|
*/
|
|
|
|
static Int128 do_ld16_beN(CPUArchState *env, MMULookupPageData *p,
|
|
|
|
uint64_t a, int mmu_idx, MemOp mop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
int size = p->size;
|
|
|
|
uint64_t b;
|
|
|
|
MemOp atom;
|
|
|
|
|
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
p->size = size - 8;
|
|
|
|
a = do_ld_mmio_beN(env, p, a, mmu_idx, MMU_DATA_LOAD, ra);
|
|
|
|
p->addr += p->size;
|
|
|
|
p->size = 8;
|
|
|
|
b = do_ld_mmio_beN(env, p, 0, mmu_idx, MMU_DATA_LOAD, ra);
|
|
|
|
return int128_make128(b, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is a given that we cross a page and therefore there is no
|
|
|
|
* atomicity for the load as a whole, but subobjects may need attention.
|
|
|
|
*/
|
|
|
|
atom = mop & MO_ATOM_MASK;
|
|
|
|
switch (atom) {
|
|
|
|
case MO_ATOM_SUBALIGN:
|
|
|
|
p->size = size - 8;
|
|
|
|
a = do_ld_parts_beN(p, a);
|
|
|
|
p->haddr += size - 8;
|
|
|
|
p->size = 8;
|
|
|
|
b = do_ld_parts_beN(p, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MO_ATOM_WITHIN16_PAIR:
|
|
|
|
/* Since size > 8, this is the half that must be atomic. */
|
|
|
|
return do_ld_whole_be16(env, ra, p, a);
|
|
|
|
|
|
|
|
case MO_ATOM_IFALIGN_PAIR:
|
|
|
|
/*
|
|
|
|
* Since size > 8, both halves are misaligned,
|
|
|
|
* and so neither is atomic.
|
|
|
|
*/
|
|
|
|
case MO_ATOM_IFALIGN:
|
|
|
|
case MO_ATOM_WITHIN16:
|
|
|
|
case MO_ATOM_NONE:
|
|
|
|
p->size = size - 8;
|
|
|
|
a = do_ld_bytes_beN(p, a);
|
|
|
|
b = ldq_be_p(p->haddr + size - 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
|
|
|
|
return int128_make128(b, a);
|
|
|
|
}
|
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
static uint8_t do_ld_1(CPUArchState *env, MMULookupPageData *p, int mmu_idx,
|
|
|
|
MMUAccessType type, uintptr_t ra)
|
|
|
|
{
|
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
return io_readx(env, p->full, mmu_idx, p->addr, ra, type, MO_UB);
|
|
|
|
} else {
|
|
|
|
return *(uint8_t *)p->haddr;
|
|
|
|
}
|
|
|
|
}
|
2019-08-24 18:51:09 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
static uint16_t do_ld_2(CPUArchState *env, MMULookupPageData *p, int mmu_idx,
|
|
|
|
MMUAccessType type, MemOp memop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
uint64_t ret;
|
2019-08-24 18:51:09 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
return io_readx(env, p->full, mmu_idx, p->addr, ra, type, memop);
|
|
|
|
}
|
2019-08-24 18:51:09 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/* Perform the load host endian, then swap if necessary. */
|
2022-10-29 07:01:04 +02:00
|
|
|
ret = load_atom_2(env, ra, p->haddr, memop);
|
2022-10-28 23:40:51 +02:00
|
|
|
if (memop & MO_BSWAP) {
|
|
|
|
ret = bswap16(ret);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2019-09-10 21:47:39 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
static uint32_t do_ld_4(CPUArchState *env, MMULookupPageData *p, int mmu_idx,
|
|
|
|
MMUAccessType type, MemOp memop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
uint32_t ret;
|
2019-09-10 21:47:39 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
return io_readx(env, p->full, mmu_idx, p->addr, ra, type, memop);
|
|
|
|
}
|
2019-09-10 21:47:39 +02:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/* Perform the load host endian. */
|
2022-10-29 07:01:04 +02:00
|
|
|
ret = load_atom_4(env, ra, p->haddr, memop);
|
2022-10-28 23:40:51 +02:00
|
|
|
if (memop & MO_BSWAP) {
|
|
|
|
ret = bswap32(ret);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2022-10-28 23:40:51 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2019-02-15 15:31:13 +01:00
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
static uint64_t do_ld_8(CPUArchState *env, MMULookupPageData *p, int mmu_idx,
|
|
|
|
MMUAccessType type, MemOp memop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
uint64_t ret;
|
|
|
|
|
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
return io_readx(env, p->full, mmu_idx, p->addr, ra, type, memop);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
/* Perform the load host endian. */
|
2022-10-29 07:01:04 +02:00
|
|
|
ret = load_atom_8(env, ra, p->haddr, memop);
|
2022-10-28 23:40:51 +02:00
|
|
|
if (memop & MO_BSWAP) {
|
|
|
|
ret = bswap64(ret);
|
|
|
|
}
|
|
|
|
return ret;
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static uint8_t do_ld1_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
|
2022-10-28 23:40:51 +02:00
|
|
|
uintptr_t ra, MMUAccessType access_type)
|
2019-04-26 05:48:57 +02:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
|
|
|
|
crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l);
|
|
|
|
tcg_debug_assert(!crosspage);
|
|
|
|
|
|
|
|
return do_ld_1(env, &l.page[0], l.mmu_idx, access_type, ra);
|
2019-04-26 05:48:57 +02:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8);
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld1_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
|
2019-04-26 05:48:57 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static uint16_t do_ld2_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
|
2022-10-28 23:40:51 +02:00
|
|
|
uintptr_t ra, MMUAccessType access_type)
|
2019-04-26 05:48:57 +02:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
uint16_t ret;
|
|
|
|
uint8_t a, b;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
a = do_ld_1(env, &l.page[0], l.mmu_idx, access_type, ra);
|
|
|
|
b = do_ld_1(env, &l.page[1], l.mmu_idx, access_type, ra);
|
|
|
|
|
|
|
|
if ((l.memop & MO_BSWAP) == MO_LE) {
|
|
|
|
ret = a | (b << 8);
|
|
|
|
} else {
|
|
|
|
ret = b | (a << 8);
|
|
|
|
}
|
|
|
|
return ret;
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld2_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
|
2019-04-26 05:48:57 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static uint32_t do_ld4_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
|
2022-10-28 23:40:51 +02:00
|
|
|
uintptr_t ra, MMUAccessType access_type)
|
2019-04-26 05:48:57 +02:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
uint32_t ret;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-10-29 07:01:04 +02:00
|
|
|
ret = do_ld_beN(env, &l.page[0], 0, l.mmu_idx, access_type, l.memop, ra);
|
|
|
|
ret = do_ld_beN(env, &l.page[1], ret, l.mmu_idx, access_type, l.memop, ra);
|
2022-10-28 23:40:51 +02:00
|
|
|
if ((l.memop & MO_BSWAP) == MO_LE) {
|
|
|
|
ret = bswap32(ret);
|
|
|
|
}
|
|
|
|
return ret;
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld4_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static uint64_t do_ld8_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi,
|
2022-10-28 23:40:51 +02:00
|
|
|
uintptr_t ra, MMUAccessType access_type)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
uint64_t ret;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-10-29 07:01:04 +02:00
|
|
|
ret = do_ld_beN(env, &l.page[0], 0, l.mmu_idx, access_type, l.memop, ra);
|
|
|
|
ret = do_ld_beN(env, &l.page[1], ret, l.mmu_idx, access_type, l.memop, ra);
|
2022-10-28 23:40:51 +02:00
|
|
|
if ((l.memop & MO_BSWAP) == MO_LE) {
|
|
|
|
ret = bswap64(ret);
|
|
|
|
}
|
|
|
|
return ret;
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld8_mmu(env, addr, oi, retaddr, MMU_DATA_LOAD);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Provide signed versions of the load routines as well. We can of course
|
|
|
|
* avoid this for 64-bit data, or for 32-bit data on 32-bit host.
|
|
|
|
*/
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
tcg_target_ulong helper_ldsb_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
return (int8_t)helper_ldub_mmu(env, addr, oi, retaddr);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
tcg_target_ulong helper_ldsw_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
return (int16_t)helper_lduw_mmu(env, addr, oi, retaddr);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
tcg_target_ulong helper_ldsl_mmu(CPUArchState *env, uint64_t addr,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
return (int32_t)helper_ldul_mmu(env, addr, oi, retaddr);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr,
|
2023-02-15 09:16:17 +01:00
|
|
|
MemOpIdx oi, uintptr_t ra)
|
|
|
|
{
|
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
uint64_t a, b;
|
|
|
|
Int128 ret;
|
|
|
|
int first;
|
|
|
|
|
|
|
|
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD, &l);
|
|
|
|
if (likely(!crosspage)) {
|
|
|
|
/* Perform the load host endian. */
|
|
|
|
if (unlikely(l.page[0].flags & TLB_MMIO)) {
|
|
|
|
QEMU_IOTHREAD_LOCK_GUARD();
|
|
|
|
a = io_readx(env, l.page[0].full, l.mmu_idx, addr,
|
|
|
|
ra, MMU_DATA_LOAD, MO_64);
|
|
|
|
b = io_readx(env, l.page[0].full, l.mmu_idx, addr + 8,
|
|
|
|
ra, MMU_DATA_LOAD, MO_64);
|
|
|
|
ret = int128_make128(HOST_BIG_ENDIAN ? b : a,
|
|
|
|
HOST_BIG_ENDIAN ? a : b);
|
|
|
|
} else {
|
|
|
|
ret = load_atom_16(env, ra, l.page[0].haddr, l.memop);
|
|
|
|
}
|
|
|
|
if (l.memop & MO_BSWAP) {
|
|
|
|
ret = bswap128(ret);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
first = l.page[0].size;
|
|
|
|
if (first == 8) {
|
|
|
|
MemOp mop8 = (l.memop & ~MO_SIZE) | MO_64;
|
|
|
|
|
|
|
|
a = do_ld_8(env, &l.page[0], l.mmu_idx, MMU_DATA_LOAD, mop8, ra);
|
|
|
|
b = do_ld_8(env, &l.page[1], l.mmu_idx, MMU_DATA_LOAD, mop8, ra);
|
|
|
|
if ((mop8 & MO_BSWAP) == MO_LE) {
|
|
|
|
ret = int128_make128(a, b);
|
|
|
|
} else {
|
|
|
|
ret = int128_make128(b, a);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first < 8) {
|
|
|
|
a = do_ld_beN(env, &l.page[0], 0, l.mmu_idx,
|
|
|
|
MMU_DATA_LOAD, l.memop, ra);
|
|
|
|
ret = do_ld16_beN(env, &l.page[1], a, l.mmu_idx, l.memop, ra);
|
|
|
|
} else {
|
|
|
|
ret = do_ld16_beN(env, &l.page[0], 0, l.mmu_idx, l.memop, ra);
|
|
|
|
b = int128_getlo(ret);
|
|
|
|
ret = int128_lshift(ret, l.page[1].size * 8);
|
|
|
|
a = int128_gethi(ret);
|
|
|
|
b = do_ld_beN(env, &l.page[1], b, l.mmu_idx,
|
|
|
|
MMU_DATA_LOAD, l.memop, ra);
|
|
|
|
ret = int128_make128(b, a);
|
|
|
|
}
|
|
|
|
if ((l.memop & MO_BSWAP) == MO_LE) {
|
|
|
|
ret = bswap128(ret);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr,
|
2023-02-15 09:16:17 +01:00
|
|
|
uint32_t oi, uintptr_t retaddr)
|
|
|
|
{
|
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
|
|
|
|
return do_ld16_mmu(env, addr, oi, retaddr);
|
|
|
|
}
|
|
|
|
|
2023-03-15 01:02:50 +01:00
|
|
|
Int128 helper_ld_i128(CPUArchState *env, uint64_t addr, uint32_t oi)
|
2023-02-15 09:16:17 +01:00
|
|
|
{
|
|
|
|
return helper_ld16_mmu(env, addr, oi, GETPC());
|
|
|
|
}
|
|
|
|
|
2019-12-09 22:49:58 +01:00
|
|
|
/*
|
|
|
|
* Load helpers for cpu_ldst.h.
|
|
|
|
*/
|
|
|
|
|
2022-10-28 23:40:51 +02:00
|
|
|
static void plugin_load_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi)
|
2019-12-09 22:49:58 +01:00
|
|
|
{
|
2021-07-26 23:48:30 +02:00
|
|
|
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R);
|
2019-12-09 22:49:58 +01:00
|
|
|
}
|
|
|
|
|
2021-07-27 19:48:55 +02:00
|
|
|
uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra)
|
2019-12-09 22:49:58 +01:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
uint8_t ret;
|
|
|
|
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_UB);
|
2022-10-28 23:40:51 +02:00
|
|
|
ret = do_ld1_mmu(env, addr, oi, ra, MMU_DATA_LOAD);
|
|
|
|
plugin_load_cb(env, addr, oi);
|
|
|
|
return ret;
|
2019-12-09 22:49:58 +01:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2019-12-11 19:33:26 +01:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
uint16_t ret;
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
|
2022-10-28 23:40:51 +02:00
|
|
|
ret = do_ld2_mmu(env, addr, oi, ra, MMU_DATA_LOAD);
|
|
|
|
plugin_load_cb(env, addr, oi);
|
|
|
|
return ret;
|
2019-12-11 19:33:26 +01:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2019-12-11 19:33:26 +01:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
uint32_t ret;
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
|
2022-10-28 23:40:51 +02:00
|
|
|
ret = do_ld4_mmu(env, addr, oi, ra, MMU_DATA_LOAD);
|
|
|
|
plugin_load_cb(env, addr, oi);
|
|
|
|
return ret;
|
2019-12-11 19:33:26 +01:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2019-12-11 19:33:26 +01:00
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
uint64_t ret;
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
|
2022-10-28 23:40:51 +02:00
|
|
|
ret = do_ld8_mmu(env, addr, oi, ra, MMU_DATA_LOAD);
|
|
|
|
plugin_load_cb(env, addr, oi);
|
|
|
|
return ret;
|
2019-12-11 19:33:26 +01:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2022-11-07 09:48:14 +01:00
|
|
|
{
|
2023-02-15 09:16:17 +01:00
|
|
|
Int128 ret;
|
2022-11-07 09:48:14 +01:00
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
|
2023-02-15 09:16:17 +01:00
|
|
|
ret = do_ld16_mmu(env, addr, oi, ra);
|
|
|
|
plugin_load_cb(env, addr, oi);
|
|
|
|
return ret;
|
2022-11-07 09:48:14 +01:00
|
|
|
}
|
|
|
|
|
2019-02-15 15:31:13 +01:00
|
|
|
/*
|
|
|
|
* Store Helpers
|
|
|
|
*/
|
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
/**
|
|
|
|
* do_st_mmio_leN:
|
|
|
|
* @env: cpu context
|
|
|
|
* @p: translation parameters
|
|
|
|
* @val_le: data to store
|
|
|
|
* @mmu_idx: virtual address context
|
|
|
|
* @ra: return address into tcg generated code, or 0
|
|
|
|
*
|
|
|
|
* Store @p->size bytes at @p->addr, which is memory-mapped i/o.
|
|
|
|
* The bytes to store are extracted in little-endian order from @val_le;
|
|
|
|
* return the bytes of @val_le beyond @p->size that have not been stored.
|
|
|
|
*/
|
|
|
|
static uint64_t do_st_mmio_leN(CPUArchState *env, MMULookupPageData *p,
|
|
|
|
uint64_t val_le, int mmu_idx, uintptr_t ra)
|
|
|
|
{
|
|
|
|
CPUTLBEntryFull *full = p->full;
|
2023-06-21 15:56:26 +02:00
|
|
|
vaddr addr = p->addr;
|
2022-10-30 02:07:32 +01:00
|
|
|
int i, size = p->size;
|
2020-07-27 00:39:53 +02:00
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
QEMU_IOTHREAD_LOCK_GUARD();
|
|
|
|
for (i = 0; i < size; i++, val_le >>= 8) {
|
|
|
|
io_writex(env, full, mmu_idx, val_le, addr + i, ra, MO_UB);
|
2020-07-27 00:39:53 +02:00
|
|
|
}
|
2022-10-30 02:07:32 +01:00
|
|
|
return val_le;
|
|
|
|
}
|
2020-07-27 00:39:53 +02:00
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
/*
|
|
|
|
* Wrapper for the above.
|
|
|
|
*/
|
|
|
|
static uint64_t do_st_leN(CPUArchState *env, MMULookupPageData *p,
|
2022-10-30 01:46:12 +02:00
|
|
|
uint64_t val_le, int mmu_idx,
|
|
|
|
MemOp mop, uintptr_t ra)
|
2022-10-30 02:07:32 +01:00
|
|
|
{
|
2022-10-30 01:46:12 +02:00
|
|
|
MemOp atom;
|
|
|
|
unsigned tmp, half_size;
|
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
return do_st_mmio_leN(env, p, val_le, mmu_idx, ra);
|
|
|
|
} else if (unlikely(p->flags & TLB_DISCARD_WRITE)) {
|
|
|
|
return val_le >> (p->size * 8);
|
2022-10-30 01:46:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is a given that we cross a page and therefore there is no atomicity
|
|
|
|
* for the store as a whole, but subobjects may need attention.
|
|
|
|
*/
|
|
|
|
atom = mop & MO_ATOM_MASK;
|
|
|
|
switch (atom) {
|
|
|
|
case MO_ATOM_SUBALIGN:
|
|
|
|
return store_parts_leN(p->haddr, p->size, val_le);
|
|
|
|
|
|
|
|
case MO_ATOM_IFALIGN_PAIR:
|
|
|
|
case MO_ATOM_WITHIN16_PAIR:
|
|
|
|
tmp = mop & MO_SIZE;
|
|
|
|
tmp = tmp ? tmp - 1 : 0;
|
|
|
|
half_size = 1 << tmp;
|
|
|
|
if (atom == MO_ATOM_IFALIGN_PAIR
|
|
|
|
? p->size == half_size
|
|
|
|
: p->size >= half_size) {
|
|
|
|
if (!HAVE_al8_fast && p->size <= 4) {
|
|
|
|
return store_whole_le4(p->haddr, p->size, val_le);
|
|
|
|
} else if (HAVE_al8) {
|
|
|
|
return store_whole_le8(p->haddr, p->size, val_le);
|
|
|
|
} else {
|
|
|
|
cpu_loop_exit_atomic(env_cpu(env), ra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case MO_ATOM_IFALIGN:
|
|
|
|
case MO_ATOM_WITHIN16:
|
|
|
|
case MO_ATOM_NONE:
|
|
|
|
return store_bytes_leN(p->haddr, p->size, val_le);
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
2020-07-27 00:39:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-15 09:16:17 +01:00
|
|
|
/*
|
|
|
|
* Wrapper for the above, for 8 < size < 16.
|
|
|
|
*/
|
|
|
|
static uint64_t do_st16_leN(CPUArchState *env, MMULookupPageData *p,
|
|
|
|
Int128 val_le, int mmu_idx,
|
|
|
|
MemOp mop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
int size = p->size;
|
|
|
|
MemOp atom;
|
|
|
|
|
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
p->size = 8;
|
|
|
|
do_st_mmio_leN(env, p, int128_getlo(val_le), mmu_idx, ra);
|
|
|
|
p->size = size - 8;
|
|
|
|
p->addr += 8;
|
|
|
|
return do_st_mmio_leN(env, p, int128_gethi(val_le), mmu_idx, ra);
|
|
|
|
} else if (unlikely(p->flags & TLB_DISCARD_WRITE)) {
|
|
|
|
return int128_gethi(val_le) >> ((size - 8) * 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is a given that we cross a page and therefore there is no atomicity
|
|
|
|
* for the store as a whole, but subobjects may need attention.
|
|
|
|
*/
|
|
|
|
atom = mop & MO_ATOM_MASK;
|
|
|
|
switch (atom) {
|
|
|
|
case MO_ATOM_SUBALIGN:
|
|
|
|
store_parts_leN(p->haddr, 8, int128_getlo(val_le));
|
|
|
|
return store_parts_leN(p->haddr + 8, p->size - 8,
|
|
|
|
int128_gethi(val_le));
|
|
|
|
|
|
|
|
case MO_ATOM_WITHIN16_PAIR:
|
|
|
|
/* Since size > 8, this is the half that must be atomic. */
|
2023-05-20 03:32:44 +02:00
|
|
|
if (!HAVE_ATOMIC128_RW) {
|
2023-02-15 09:16:17 +01:00
|
|
|
cpu_loop_exit_atomic(env_cpu(env), ra);
|
|
|
|
}
|
|
|
|
return store_whole_le16(p->haddr, p->size, val_le);
|
|
|
|
|
|
|
|
case MO_ATOM_IFALIGN_PAIR:
|
|
|
|
/*
|
|
|
|
* Since size > 8, both halves are misaligned,
|
|
|
|
* and so neither is atomic.
|
|
|
|
*/
|
|
|
|
case MO_ATOM_IFALIGN:
|
2023-06-19 15:23:14 +02:00
|
|
|
case MO_ATOM_WITHIN16:
|
2023-02-15 09:16:17 +01:00
|
|
|
case MO_ATOM_NONE:
|
|
|
|
stq_le_p(p->haddr, int128_getlo(val_le));
|
|
|
|
return store_bytes_leN(p->haddr + 8, p->size - 8,
|
|
|
|
int128_gethi(val_le));
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
static void do_st_1(CPUArchState *env, MMULookupPageData *p, uint8_t val,
|
|
|
|
int mmu_idx, uintptr_t ra)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-10-30 02:07:32 +01:00
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
io_writex(env, p->full, mmu_idx, val, p->addr, ra, MO_UB);
|
|
|
|
} else if (unlikely(p->flags & TLB_DISCARD_WRITE)) {
|
|
|
|
/* nothing */
|
|
|
|
} else {
|
|
|
|
*(uint8_t *)p->haddr = val;
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2022-10-30 02:07:32 +01:00
|
|
|
}
|
2019-02-15 15:31:13 +01:00
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
static void do_st_2(CPUArchState *env, MMULookupPageData *p, uint16_t val,
|
|
|
|
int mmu_idx, MemOp memop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
io_writex(env, p->full, mmu_idx, val, p->addr, ra, memop);
|
|
|
|
} else if (unlikely(p->flags & TLB_DISCARD_WRITE)) {
|
|
|
|
/* nothing */
|
|
|
|
} else {
|
|
|
|
/* Swap to host endian if necessary, then store. */
|
|
|
|
if (memop & MO_BSWAP) {
|
|
|
|
val = bswap16(val);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2022-10-30 01:46:12 +02:00
|
|
|
store_atom_2(env, ra, p->haddr, memop, val);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2022-10-30 02:07:32 +01:00
|
|
|
}
|
2019-02-15 15:31:13 +01:00
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
static void do_st_4(CPUArchState *env, MMULookupPageData *p, uint32_t val,
|
|
|
|
int mmu_idx, MemOp memop, uintptr_t ra)
|
|
|
|
{
|
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
io_writex(env, p->full, mmu_idx, val, p->addr, ra, memop);
|
|
|
|
} else if (unlikely(p->flags & TLB_DISCARD_WRITE)) {
|
|
|
|
/* nothing */
|
|
|
|
} else {
|
|
|
|
/* Swap to host endian if necessary, then store. */
|
|
|
|
if (memop & MO_BSWAP) {
|
|
|
|
val = bswap32(val);
|
2019-09-10 21:47:39 +02:00
|
|
|
}
|
2022-10-30 01:46:12 +02:00
|
|
|
store_atom_4(env, ra, p->haddr, memop, val);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
static void do_st_8(CPUArchState *env, MMULookupPageData *p, uint64_t val,
|
|
|
|
int mmu_idx, MemOp memop, uintptr_t ra)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-10-30 02:07:32 +01:00
|
|
|
if (unlikely(p->flags & TLB_MMIO)) {
|
|
|
|
io_writex(env, p->full, mmu_idx, val, p->addr, ra, memop);
|
|
|
|
} else if (unlikely(p->flags & TLB_DISCARD_WRITE)) {
|
|
|
|
/* nothing */
|
|
|
|
} else {
|
|
|
|
/* Swap to host endian if necessary, then store. */
|
|
|
|
if (memop & MO_BSWAP) {
|
|
|
|
val = bswap64(val);
|
|
|
|
}
|
2022-10-30 01:46:12 +02:00
|
|
|
store_atom_8(env, ra, p->haddr, memop, val);
|
2022-10-30 02:07:32 +01:00
|
|
|
}
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2021-07-27 19:48:55 +02:00
|
|
|
{
|
2022-10-30 02:07:32 +01:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8);
|
2022-10-30 02:07:32 +01:00
|
|
|
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);
|
2021-07-27 19:48:55 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static void do_st2_mmu(CPUArchState *env, vaddr addr, uint16_t val,
|
2022-10-30 02:07:32 +01:00
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2021-07-27 19:48:55 +02:00
|
|
|
{
|
2022-10-30 02:07:32 +01:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
uint8_t a, b;
|
|
|
|
|
|
|
|
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);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((l.memop & MO_BSWAP) == MO_LE) {
|
|
|
|
a = val, b = val >> 8;
|
|
|
|
} else {
|
|
|
|
b = val, a = val >> 8;
|
|
|
|
}
|
|
|
|
do_st_1(env, &l.page[0], a, l.mmu_idx, ra);
|
|
|
|
do_st_1(env, &l.page[1], b, l.mmu_idx, ra);
|
2021-07-27 19:48:55 +02:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
|
2022-10-30 02:07:32 +01:00
|
|
|
do_st2_mmu(env, addr, val, oi, retaddr);
|
2021-07-27 19:48:55 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static void do_st4_mmu(CPUArchState *env, vaddr addr, uint32_t val,
|
2022-10-30 02:07:32 +01:00
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2021-07-27 19:48:55 +02:00
|
|
|
{
|
2022-10-30 02:07:32 +01:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
|
|
|
|
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);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Swap to little endian for simplicity, then store by bytes. */
|
|
|
|
if ((l.memop & MO_BSWAP) != MO_LE) {
|
|
|
|
val = bswap32(val);
|
|
|
|
}
|
2022-10-30 01:46:12 +02:00
|
|
|
val = do_st_leN(env, &l.page[0], val, l.mmu_idx, l.memop, ra);
|
|
|
|
(void) do_st_leN(env, &l.page[1], val, l.mmu_idx, l.memop, ra);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2021-07-27 19:48:55 +02:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
|
2022-10-30 02:07:32 +01:00
|
|
|
do_st4_mmu(env, addr, val, oi, retaddr);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static void do_st8_mmu(CPUArchState *env, vaddr addr, uint64_t val,
|
2022-10-30 02:07:32 +01:00
|
|
|
MemOpIdx oi, uintptr_t ra)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-10-30 02:07:32 +01:00
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
|
|
|
|
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);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Swap to little endian for simplicity, then store by bytes. */
|
|
|
|
if ((l.memop & MO_BSWAP) != MO_LE) {
|
|
|
|
val = bswap64(val);
|
|
|
|
}
|
2022-10-30 01:46:12 +02:00
|
|
|
val = do_st_leN(env, &l.page[0], val, l.mmu_idx, l.memop, ra);
|
|
|
|
(void) do_st_leN(env, &l.page[1], val, l.mmu_idx, l.memop, ra);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val,
|
2022-11-01 02:51:04 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
|
2022-10-30 02:07:32 +01:00
|
|
|
do_st8_mmu(env, addr, val, oi, retaddr);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2014-03-28 17:55:24 +01:00
|
|
|
|
2023-06-21 15:56:26 +02:00
|
|
|
static void do_st16_mmu(CPUArchState *env, vaddr addr, Int128 val,
|
2023-02-15 09:16:17 +01:00
|
|
|
MemOpIdx oi, uintptr_t ra)
|
|
|
|
{
|
|
|
|
MMULookupLocals l;
|
|
|
|
bool crosspage;
|
|
|
|
uint64_t a, b;
|
|
|
|
int first;
|
|
|
|
|
|
|
|
crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l);
|
|
|
|
if (likely(!crosspage)) {
|
|
|
|
/* Swap to host endian if necessary, then store. */
|
|
|
|
if (l.memop & MO_BSWAP) {
|
|
|
|
val = bswap128(val);
|
|
|
|
}
|
|
|
|
if (unlikely(l.page[0].flags & TLB_MMIO)) {
|
|
|
|
QEMU_IOTHREAD_LOCK_GUARD();
|
|
|
|
if (HOST_BIG_ENDIAN) {
|
|
|
|
b = int128_getlo(val), a = int128_gethi(val);
|
|
|
|
} else {
|
|
|
|
a = int128_getlo(val), b = int128_gethi(val);
|
|
|
|
}
|
|
|
|
io_writex(env, l.page[0].full, l.mmu_idx, a, addr, ra, MO_64);
|
|
|
|
io_writex(env, l.page[0].full, l.mmu_idx, b, addr + 8, ra, MO_64);
|
|
|
|
} else if (unlikely(l.page[0].flags & TLB_DISCARD_WRITE)) {
|
|
|
|
/* nothing */
|
|
|
|
} else {
|
|
|
|
store_atom_16(env, ra, l.page[0].haddr, l.memop, val);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
first = l.page[0].size;
|
|
|
|
if (first == 8) {
|
|
|
|
MemOp mop8 = (l.memop & ~(MO_SIZE | MO_BSWAP)) | MO_64;
|
|
|
|
|
|
|
|
if (l.memop & MO_BSWAP) {
|
|
|
|
val = bswap128(val);
|
|
|
|
}
|
|
|
|
if (HOST_BIG_ENDIAN) {
|
|
|
|
b = int128_getlo(val), a = int128_gethi(val);
|
|
|
|
} else {
|
|
|
|
a = int128_getlo(val), b = int128_gethi(val);
|
|
|
|
}
|
|
|
|
do_st_8(env, &l.page[0], a, l.mmu_idx, mop8, ra);
|
|
|
|
do_st_8(env, &l.page[1], b, l.mmu_idx, mop8, ra);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((l.memop & MO_BSWAP) != MO_LE) {
|
|
|
|
val = bswap128(val);
|
|
|
|
}
|
|
|
|
if (first < 8) {
|
|
|
|
do_st_leN(env, &l.page[0], int128_getlo(val), l.mmu_idx, l.memop, ra);
|
|
|
|
val = int128_urshift(val, first * 8);
|
|
|
|
do_st16_leN(env, &l.page[1], val, l.mmu_idx, l.memop, ra);
|
|
|
|
} else {
|
|
|
|
b = do_st16_leN(env, &l.page[0], val, l.mmu_idx, l.memop, ra);
|
|
|
|
do_st_leN(env, &l.page[1], b, l.mmu_idx, l.memop, ra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-26 23:09:47 +02:00
|
|
|
void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val,
|
2023-02-15 09:16:17 +01:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
|
|
|
{
|
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
|
|
|
|
do_st16_mmu(env, addr, val, oi, retaddr);
|
|
|
|
}
|
|
|
|
|
2023-03-15 01:02:50 +01:00
|
|
|
void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi)
|
2023-02-15 09:16:17 +01:00
|
|
|
{
|
|
|
|
helper_st16_mmu(env, addr, val, oi, GETPC());
|
|
|
|
}
|
|
|
|
|
2019-12-09 22:49:58 +01:00
|
|
|
/*
|
|
|
|
* Store Helpers for cpu_ldst.h
|
|
|
|
*/
|
|
|
|
|
2022-10-30 02:07:32 +01:00
|
|
|
static void plugin_store_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi)
|
2021-07-27 19:48:55 +02:00
|
|
|
{
|
2021-07-26 23:48:30 +02:00
|
|
|
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W);
|
2019-12-09 22:49:58 +01:00
|
|
|
}
|
|
|
|
|
2021-07-27 19:48:55 +02:00
|
|
|
void cpu_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-12-09 22:49:58 +01:00
|
|
|
{
|
2022-11-01 02:51:04 +01:00
|
|
|
helper_stb_mmu(env, addr, val, oi, retaddr);
|
2022-10-30 02:07:32 +01:00
|
|
|
plugin_store_cb(env, addr, oi);
|
2019-12-09 22:49:58 +01:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
void cpu_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2019-12-09 22:49:58 +01:00
|
|
|
{
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
|
2022-11-01 02:51:04 +01:00
|
|
|
do_st2_mmu(env, addr, val, oi, retaddr);
|
2022-10-30 02:07:32 +01:00
|
|
|
plugin_store_cb(env, addr, oi);
|
2020-05-08 17:43:46 +02:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
void cpu_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
|
2021-07-27 19:48:55 +02:00
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2020-05-08 17:43:46 +02:00
|
|
|
{
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
|
2022-11-01 02:51:04 +01:00
|
|
|
do_st4_mmu(env, addr, val, oi, retaddr);
|
2022-10-30 02:07:32 +01:00
|
|
|
plugin_store_cb(env, addr, oi);
|
2020-05-08 17:43:46 +02:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
void cpu_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2020-05-08 17:43:46 +02:00
|
|
|
{
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
|
2022-11-01 02:51:04 +01:00
|
|
|
do_st8_mmu(env, addr, val, oi, retaddr);
|
2022-10-30 02:07:32 +01:00
|
|
|
plugin_store_cb(env, addr, oi);
|
2019-12-09 22:49:58 +01:00
|
|
|
}
|
|
|
|
|
2023-05-20 02:29:27 +02:00
|
|
|
void cpu_st16_mmu(CPUArchState *env, target_ulong addr, Int128 val,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
2022-11-07 09:48:14 +01:00
|
|
|
{
|
2023-05-20 02:29:27 +02:00
|
|
|
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
|
2023-02-15 09:16:17 +01:00
|
|
|
do_st16_mmu(env, addr, val, oi, retaddr);
|
|
|
|
plugin_store_cb(env, addr, oi);
|
2022-11-07 09:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-07-27 19:48:55 +02:00
|
|
|
#include "ldst_common.c.inc"
|
2019-12-11 19:33:26 +01:00
|
|
|
|
2021-07-16 23:20:49 +02:00
|
|
|
/*
|
|
|
|
* First set of functions passes in OI and RETADDR.
|
|
|
|
* This makes them callable from other helpers.
|
|
|
|
*/
|
2016-06-28 20:37:27 +02:00
|
|
|
|
|
|
|
#define ATOMIC_NAME(X) \
|
2021-07-16 23:20:49 +02:00
|
|
|
glue(glue(glue(cpu_atomic_ ## X, SUFFIX), END), _mmu)
|
2021-07-17 02:49:09 +02:00
|
|
|
|
2019-09-22 03:47:59 +02:00
|
|
|
#define ATOMIC_MMU_CLEANUP
|
2016-06-28 20:37:27 +02:00
|
|
|
|
2020-02-04 12:41:01 +01:00
|
|
|
#include "atomic_common.c.inc"
|
2016-06-28 20:37:27 +02:00
|
|
|
|
|
|
|
#define DATA_SIZE 1
|
|
|
|
#include "atomic_template.h"
|
|
|
|
|
|
|
|
#define DATA_SIZE 2
|
|
|
|
#include "atomic_template.h"
|
|
|
|
|
|
|
|
#define DATA_SIZE 4
|
|
|
|
#include "atomic_template.h"
|
|
|
|
|
2016-09-02 21:23:57 +02:00
|
|
|
#ifdef CONFIG_ATOMIC64
|
2016-06-28 20:37:27 +02:00
|
|
|
#define DATA_SIZE 8
|
|
|
|
#include "atomic_template.h"
|
2016-09-02 21:23:57 +02:00
|
|
|
#endif
|
2016-06-28 20:37:27 +02:00
|
|
|
|
2023-05-20 03:02:19 +02:00
|
|
|
#if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128)
|
2016-06-30 06:10:59 +02:00
|
|
|
#define DATA_SIZE 16
|
|
|
|
#include "atomic_template.h"
|
|
|
|
#endif
|
|
|
|
|
2016-06-28 20:37:27 +02:00
|
|
|
/* Code access functions. */
|
|
|
|
|
2019-12-11 20:25:10 +01:00
|
|
|
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2021-07-26 00:06:49 +02:00
|
|
|
MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true));
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld1_mmu(env, addr, oi, 0, MMU_INST_FETCH);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2019-12-11 20:25:10 +01:00
|
|
|
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2021-07-26 00:06:49 +02:00
|
|
|
MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(env, true));
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld2_mmu(env, addr, oi, 0, MMU_INST_FETCH);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2012-04-09 18:50:52 +02:00
|
|
|
|
2019-12-11 20:25:10 +01:00
|
|
|
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2021-07-26 00:06:49 +02:00
|
|
|
MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true));
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld4_mmu(env, addr, oi, 0, MMU_INST_FETCH);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
|
|
|
|
2019-12-11 20:25:10 +01:00
|
|
|
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr)
|
2019-02-15 15:31:13 +01:00
|
|
|
{
|
2022-01-06 22:00:51 +01:00
|
|
|
MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(env, true));
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld8_mmu(env, addr, oi, 0, MMU_INST_FETCH);
|
2019-02-15 15:31:13 +01:00
|
|
|
}
|
2023-04-12 13:43:16 +02:00
|
|
|
|
|
|
|
uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld1_mmu(env, addr, oi, retaddr, MMU_INST_FETCH);
|
2023-04-12 13:43:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld2_mmu(env, addr, oi, retaddr, MMU_INST_FETCH);
|
2023-04-12 13:43:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld4_mmu(env, addr, oi, retaddr, MMU_INST_FETCH);
|
2023-04-12 13:43:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr,
|
|
|
|
MemOpIdx oi, uintptr_t retaddr)
|
|
|
|
{
|
2022-10-28 23:40:51 +02:00
|
|
|
return do_ld8_mmu(env, addr, oi, retaddr, MMU_INST_FETCH);
|
2023-04-12 13:43:16 +02:00
|
|
|
}
|