1a3ef070eSClaudio Fontana /*
2a3ef070eSClaudio Fontana * ARM v8.5-MemTag Operations
3a3ef070eSClaudio Fontana *
4a3ef070eSClaudio Fontana * Copyright (c) 2020 Linaro, Ltd.
5a3ef070eSClaudio Fontana *
6a3ef070eSClaudio Fontana * This library is free software; you can redistribute it and/or
7a3ef070eSClaudio Fontana * modify it under the terms of the GNU Lesser General Public
8a3ef070eSClaudio Fontana * License as published by the Free Software Foundation; either
9a3ef070eSClaudio Fontana * version 2.1 of the License, or (at your option) any later version.
10a3ef070eSClaudio Fontana *
11a3ef070eSClaudio Fontana * This library is distributed in the hope that it will be useful,
12a3ef070eSClaudio Fontana * but WITHOUT ANY WARRANTY; without even the implied warranty of
13a3ef070eSClaudio Fontana * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14a3ef070eSClaudio Fontana * Lesser General Public License for more details.
15a3ef070eSClaudio Fontana *
16a3ef070eSClaudio Fontana * You should have received a copy of the GNU Lesser General Public
17a3ef070eSClaudio Fontana * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18a3ef070eSClaudio Fontana */
19a3ef070eSClaudio Fontana
20a3ef070eSClaudio Fontana #include "qemu/osdep.h"
21a3ef070eSClaudio Fontana #include "qemu/log.h"
22a3ef070eSClaudio Fontana #include "cpu.h"
23a3ef070eSClaudio Fontana #include "internals.h"
24a3ef070eSClaudio Fontana #include "exec/exec-all.h"
2574781c08SPhilippe Mathieu-Daudé #include "exec/page-protection.h"
26a3ef070eSClaudio Fontana #include "exec/ram_addr.h"
27a3ef070eSClaudio Fontana #include "exec/cpu_ldst.h"
28a3ef070eSClaudio Fontana #include "exec/helper-proto.h"
296eece7f5SPhilippe Mathieu-Daudé #include "hw/core/tcg-cpu-ops.h"
30a3ef070eSClaudio Fontana #include "qapi/error.h"
31a3ef070eSClaudio Fontana #include "qemu/guest-random.h"
32*0c9b437cSGustavo Romero #include "mte_helper.h"
33a3ef070eSClaudio Fontana
34a3ef070eSClaudio Fontana
choose_nonexcluded_tag(int tag,int offset,uint16_t exclude)35a3ef070eSClaudio Fontana static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude)
36a3ef070eSClaudio Fontana {
37a3ef070eSClaudio Fontana if (exclude == 0xffff) {
38a3ef070eSClaudio Fontana return 0;
39a3ef070eSClaudio Fontana }
40a3ef070eSClaudio Fontana if (offset == 0) {
41a3ef070eSClaudio Fontana while (exclude & (1 << tag)) {
42a3ef070eSClaudio Fontana tag = (tag + 1) & 15;
43a3ef070eSClaudio Fontana }
44a3ef070eSClaudio Fontana } else {
45a3ef070eSClaudio Fontana do {
46a3ef070eSClaudio Fontana do {
47a3ef070eSClaudio Fontana tag = (tag + 1) & 15;
48a3ef070eSClaudio Fontana } while (exclude & (1 << tag));
49a3ef070eSClaudio Fontana } while (--offset > 0);
50a3ef070eSClaudio Fontana }
51a3ef070eSClaudio Fontana return tag;
52a3ef070eSClaudio Fontana }
53a3ef070eSClaudio Fontana
allocation_tag_mem_probe(CPUARMState * env,int ptr_mmu_idx,uint64_t ptr,MMUAccessType ptr_access,int ptr_size,MMUAccessType tag_access,bool probe,uintptr_t ra)54*0c9b437cSGustavo Romero uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx,
55a3ef070eSClaudio Fontana uint64_t ptr, MMUAccessType ptr_access,
56a3ef070eSClaudio Fontana int ptr_size, MMUAccessType tag_access,
57aa03378bSPeter Maydell bool probe, uintptr_t ra)
58a3ef070eSClaudio Fontana {
59a3ef070eSClaudio Fontana #ifdef CONFIG_USER_ONLY
60a3ef070eSClaudio Fontana uint64_t clean_ptr = useronly_clean_ptr(ptr);
61a3ef070eSClaudio Fontana int flags = page_get_flags(clean_ptr);
62a3ef070eSClaudio Fontana uint8_t *tags;
63a3ef070eSClaudio Fontana uintptr_t index;
64a3ef070eSClaudio Fontana
65aa03378bSPeter Maydell assert(!(probe && ra));
66aa03378bSPeter Maydell
67a3ef070eSClaudio Fontana if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) {
6841bfb670SGustavo Romero if (probe) {
6941bfb670SGustavo Romero return NULL;
7041bfb670SGustavo Romero }
71a3ef070eSClaudio Fontana cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access,
72a3ef070eSClaudio Fontana !(flags & PAGE_VALID), ra);
73a3ef070eSClaudio Fontana }
74a3ef070eSClaudio Fontana
75a3ef070eSClaudio Fontana /* Require both MAP_ANON and PROT_MTE for the page. */
76a3ef070eSClaudio Fontana if (!(flags & PAGE_ANON) || !(flags & PAGE_MTE)) {
77a3ef070eSClaudio Fontana return NULL;
78a3ef070eSClaudio Fontana }
79a3ef070eSClaudio Fontana
80a3ef070eSClaudio Fontana tags = page_get_target_data(clean_ptr);
81a3ef070eSClaudio Fontana
82a3ef070eSClaudio Fontana index = extract32(ptr, LOG2_TAG_GRANULE + 1,
83a3ef070eSClaudio Fontana TARGET_PAGE_BITS - LOG2_TAG_GRANULE - 1);
84a3ef070eSClaudio Fontana return tags + index;
85a3ef070eSClaudio Fontana #else
86a3ef070eSClaudio Fontana CPUTLBEntryFull *full;
87a3ef070eSClaudio Fontana MemTxAttrs attrs;
88a3ef070eSClaudio Fontana int in_page, flags;
89a3ef070eSClaudio Fontana hwaddr ptr_paddr, tag_paddr, xlat;
90a3ef070eSClaudio Fontana MemoryRegion *mr;
91a3ef070eSClaudio Fontana ARMASIdx tag_asi;
92a3ef070eSClaudio Fontana AddressSpace *tag_as;
93a3ef070eSClaudio Fontana void *host;
94a3ef070eSClaudio Fontana
95a3ef070eSClaudio Fontana /*
96a3ef070eSClaudio Fontana * Probe the first byte of the virtual address. This raises an
97a3ef070eSClaudio Fontana * exception for inaccessible pages, and resolves the virtual address
98a3ef070eSClaudio Fontana * into the softmmu tlb.
99a3ef070eSClaudio Fontana *
100aa03378bSPeter Maydell * When RA == 0, this is either a pure probe or a no-fault-expected probe.
101aa03378bSPeter Maydell * Indicate to probe_access_flags no-fault, then either return NULL
102aa03378bSPeter Maydell * for the pure probe, or assert that we received a valid page for the
103aa03378bSPeter Maydell * no-fault-expected probe.
104a3ef070eSClaudio Fontana */
105d507e6c5SRichard Henderson flags = probe_access_full(env, ptr, 0, ptr_access, ptr_mmu_idx,
106a3ef070eSClaudio Fontana ra == 0, &host, &full, ra);
107aa03378bSPeter Maydell if (probe && (flags & TLB_INVALID_MASK)) {
108aa03378bSPeter Maydell return NULL;
109aa03378bSPeter Maydell }
110a3ef070eSClaudio Fontana assert(!(flags & TLB_INVALID_MASK));
111a3ef070eSClaudio Fontana
112a3ef070eSClaudio Fontana /* If the virtual page MemAttr != Tagged, access unchecked. */
113a81fef4bSAnton Johansson if (full->extra.arm.pte_attrs != 0xf0) {
114a3ef070eSClaudio Fontana return NULL;
115a3ef070eSClaudio Fontana }
116a3ef070eSClaudio Fontana
117a3ef070eSClaudio Fontana /*
118a3ef070eSClaudio Fontana * If not backed by host ram, there is no tag storage: access unchecked.
119a3ef070eSClaudio Fontana * This is probably a guest os bug though, so log it.
120a3ef070eSClaudio Fontana */
121a3ef070eSClaudio Fontana if (unlikely(flags & TLB_MMIO)) {
122a3ef070eSClaudio Fontana qemu_log_mask(LOG_GUEST_ERROR,
123a3ef070eSClaudio Fontana "Page @ 0x%" PRIx64 " indicates Tagged Normal memory "
124a3ef070eSClaudio Fontana "but is not backed by host ram\n", ptr);
125a3ef070eSClaudio Fontana return NULL;
126a3ef070eSClaudio Fontana }
127a3ef070eSClaudio Fontana
128a3ef070eSClaudio Fontana /*
129a3ef070eSClaudio Fontana * Remember these values across the second lookup below,
130a3ef070eSClaudio Fontana * which may invalidate this pointer via tlb resize.
131a3ef070eSClaudio Fontana */
132a3ef070eSClaudio Fontana ptr_paddr = full->phys_addr | (ptr & ~TARGET_PAGE_MASK);
133a3ef070eSClaudio Fontana attrs = full->attrs;
134a3ef070eSClaudio Fontana full = NULL;
135a3ef070eSClaudio Fontana
136a3ef070eSClaudio Fontana /*
137a3ef070eSClaudio Fontana * The Normal memory access can extend to the next page. E.g. a single
138a3ef070eSClaudio Fontana * 8-byte access to the last byte of a page will check only the last
139a3ef070eSClaudio Fontana * tag on the first page.
140a3ef070eSClaudio Fontana * Any page access exception has priority over tag check exception.
141a3ef070eSClaudio Fontana */
142a3ef070eSClaudio Fontana in_page = -(ptr | TARGET_PAGE_MASK);
143a3ef070eSClaudio Fontana if (unlikely(ptr_size > in_page)) {
144d507e6c5SRichard Henderson flags |= probe_access_full(env, ptr + in_page, 0, ptr_access,
145a3ef070eSClaudio Fontana ptr_mmu_idx, ra == 0, &host, &full, ra);
146a3ef070eSClaudio Fontana assert(!(flags & TLB_INVALID_MASK));
147a3ef070eSClaudio Fontana }
148a3ef070eSClaudio Fontana
149a3ef070eSClaudio Fontana /* Any debug exception has priority over a tag check exception. */
150aa03378bSPeter Maydell if (!probe && unlikely(flags & TLB_WATCHPOINT)) {
151a3ef070eSClaudio Fontana int wp = ptr_access == MMU_DATA_LOAD ? BP_MEM_READ : BP_MEM_WRITE;
152a3ef070eSClaudio Fontana assert(ra != 0);
153a3ef070eSClaudio Fontana cpu_check_watchpoint(env_cpu(env), ptr, ptr_size, attrs, wp, ra);
154a3ef070eSClaudio Fontana }
155a3ef070eSClaudio Fontana
156a3ef070eSClaudio Fontana /* Convert to the physical address in tag space. */
157a3ef070eSClaudio Fontana tag_paddr = ptr_paddr >> (LOG2_TAG_GRANULE + 1);
158a3ef070eSClaudio Fontana
159a3ef070eSClaudio Fontana /* Look up the address in tag space. */
160a3ef070eSClaudio Fontana tag_asi = attrs.secure ? ARMASIdx_TagS : ARMASIdx_TagNS;
161a3ef070eSClaudio Fontana tag_as = cpu_get_address_space(env_cpu(env), tag_asi);
162a3ef070eSClaudio Fontana mr = address_space_translate(tag_as, tag_paddr, &xlat, NULL,
163a3ef070eSClaudio Fontana tag_access == MMU_DATA_STORE, attrs);
164a3ef070eSClaudio Fontana
165a3ef070eSClaudio Fontana /*
166a3ef070eSClaudio Fontana * Note that @mr will never be NULL. If there is nothing in the address
167a3ef070eSClaudio Fontana * space at @tag_paddr, the translation will return the unallocated memory
168a3ef070eSClaudio Fontana * region. For our purposes, the result must be ram.
169a3ef070eSClaudio Fontana */
170a3ef070eSClaudio Fontana if (unlikely(!memory_region_is_ram(mr))) {
171a3ef070eSClaudio Fontana /* ??? Failure is a board configuration error. */
172a3ef070eSClaudio Fontana qemu_log_mask(LOG_UNIMP,
173a3ef070eSClaudio Fontana "Tag Memory @ 0x%" HWADDR_PRIx " not found for "
174a3ef070eSClaudio Fontana "Normal Memory @ 0x%" HWADDR_PRIx "\n",
175a3ef070eSClaudio Fontana tag_paddr, ptr_paddr);
176a3ef070eSClaudio Fontana return NULL;
177a3ef070eSClaudio Fontana }
178a3ef070eSClaudio Fontana
179a3ef070eSClaudio Fontana /*
180a3ef070eSClaudio Fontana * Ensure the tag memory is dirty on write, for migration.
181a3ef070eSClaudio Fontana * Tag memory can never contain code or display memory (vga).
182a3ef070eSClaudio Fontana */
183a3ef070eSClaudio Fontana if (tag_access == MMU_DATA_STORE) {
184a3ef070eSClaudio Fontana ram_addr_t tag_ra = memory_region_get_ram_addr(mr) + xlat;
185a3ef070eSClaudio Fontana cpu_physical_memory_set_dirty_flag(tag_ra, DIRTY_MEMORY_MIGRATION);
186a3ef070eSClaudio Fontana }
187a3ef070eSClaudio Fontana
188a3ef070eSClaudio Fontana return memory_region_get_ram_ptr(mr) + xlat;
189a3ef070eSClaudio Fontana #endif
190a3ef070eSClaudio Fontana }
191a3ef070eSClaudio Fontana
allocation_tag_mem(CPUARMState * env,int ptr_mmu_idx,uint64_t ptr,MMUAccessType ptr_access,int ptr_size,MMUAccessType tag_access,uintptr_t ra)192aa03378bSPeter Maydell static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
193aa03378bSPeter Maydell uint64_t ptr, MMUAccessType ptr_access,
194aa03378bSPeter Maydell int ptr_size, MMUAccessType tag_access,
195aa03378bSPeter Maydell uintptr_t ra)
196aa03378bSPeter Maydell {
197aa03378bSPeter Maydell return allocation_tag_mem_probe(env, ptr_mmu_idx, ptr, ptr_access,
198aa03378bSPeter Maydell ptr_size, tag_access, false, ra);
199aa03378bSPeter Maydell }
200aa03378bSPeter Maydell
HELPER(irg)201a3ef070eSClaudio Fontana uint64_t HELPER(irg)(CPUARMState *env, uint64_t rn, uint64_t rm)
202a3ef070eSClaudio Fontana {
203a3ef070eSClaudio Fontana uint16_t exclude = extract32(rm | env->cp15.gcr_el1, 0, 16);
204a3ef070eSClaudio Fontana int rrnd = extract32(env->cp15.gcr_el1, 16, 1);
205a3ef070eSClaudio Fontana int start = extract32(env->cp15.rgsr_el1, 0, 4);
206a3ef070eSClaudio Fontana int seed = extract32(env->cp15.rgsr_el1, 8, 16);
207a3ef070eSClaudio Fontana int offset, i, rtag;
208a3ef070eSClaudio Fontana
209a3ef070eSClaudio Fontana /*
210a3ef070eSClaudio Fontana * Our IMPDEF choice for GCR_EL1.RRND==1 is to continue to use the
211a3ef070eSClaudio Fontana * deterministic algorithm. Except that with RRND==1 the kernel is
212a3ef070eSClaudio Fontana * not required to have set RGSR_EL1.SEED != 0, which is required for
213a3ef070eSClaudio Fontana * the deterministic algorithm to function. So we force a non-zero
214a3ef070eSClaudio Fontana * SEED for that case.
215a3ef070eSClaudio Fontana */
216a3ef070eSClaudio Fontana if (unlikely(seed == 0) && rrnd) {
217a3ef070eSClaudio Fontana do {
218a3ef070eSClaudio Fontana Error *err = NULL;
219a3ef070eSClaudio Fontana uint16_t two;
220a3ef070eSClaudio Fontana
221a3ef070eSClaudio Fontana if (qemu_guest_getrandom(&two, sizeof(two), &err) < 0) {
222a3ef070eSClaudio Fontana /*
223a3ef070eSClaudio Fontana * Failed, for unknown reasons in the crypto subsystem.
224a3ef070eSClaudio Fontana * Best we can do is log the reason and use a constant seed.
225a3ef070eSClaudio Fontana */
226a3ef070eSClaudio Fontana qemu_log_mask(LOG_UNIMP, "IRG: Crypto failure: %s\n",
227a3ef070eSClaudio Fontana error_get_pretty(err));
228a3ef070eSClaudio Fontana error_free(err);
229a3ef070eSClaudio Fontana two = 1;
230a3ef070eSClaudio Fontana }
231a3ef070eSClaudio Fontana seed = two;
232a3ef070eSClaudio Fontana } while (seed == 0);
233a3ef070eSClaudio Fontana }
234a3ef070eSClaudio Fontana
235a3ef070eSClaudio Fontana /* RandomTag */
236a3ef070eSClaudio Fontana for (i = offset = 0; i < 4; ++i) {
237a3ef070eSClaudio Fontana /* NextRandomTagBit */
238a3ef070eSClaudio Fontana int top = (extract32(seed, 5, 1) ^ extract32(seed, 3, 1) ^
239a3ef070eSClaudio Fontana extract32(seed, 2, 1) ^ extract32(seed, 0, 1));
240a3ef070eSClaudio Fontana seed = (top << 15) | (seed >> 1);
241a3ef070eSClaudio Fontana offset |= top << i;
242a3ef070eSClaudio Fontana }
243a3ef070eSClaudio Fontana rtag = choose_nonexcluded_tag(start, offset, exclude);
244a3ef070eSClaudio Fontana env->cp15.rgsr_el1 = rtag | (seed << 8);
245a3ef070eSClaudio Fontana
246a3ef070eSClaudio Fontana return address_with_allocation_tag(rn, rtag);
247a3ef070eSClaudio Fontana }
248a3ef070eSClaudio Fontana
HELPER(addsubg)249a3ef070eSClaudio Fontana uint64_t HELPER(addsubg)(CPUARMState *env, uint64_t ptr,
250a3ef070eSClaudio Fontana int32_t offset, uint32_t tag_offset)
251a3ef070eSClaudio Fontana {
252a3ef070eSClaudio Fontana int start_tag = allocation_tag_from_addr(ptr);
253a3ef070eSClaudio Fontana uint16_t exclude = extract32(env->cp15.gcr_el1, 0, 16);
254a3ef070eSClaudio Fontana int rtag = choose_nonexcluded_tag(start_tag, tag_offset, exclude);
255a3ef070eSClaudio Fontana
256a3ef070eSClaudio Fontana return address_with_allocation_tag(ptr + offset, rtag);
257a3ef070eSClaudio Fontana }
258a3ef070eSClaudio Fontana
load_tag1(uint64_t ptr,uint8_t * mem)259*0c9b437cSGustavo Romero int load_tag1(uint64_t ptr, uint8_t *mem)
260a3ef070eSClaudio Fontana {
261a3ef070eSClaudio Fontana int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4;
262a3ef070eSClaudio Fontana return extract32(*mem, ofs, 4);
263a3ef070eSClaudio Fontana }
264a3ef070eSClaudio Fontana
HELPER(ldg)265a3ef070eSClaudio Fontana uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt)
266a3ef070eSClaudio Fontana {
267b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
268a3ef070eSClaudio Fontana uint8_t *mem;
269a3ef070eSClaudio Fontana int rtag = 0;
270a3ef070eSClaudio Fontana
271a3ef070eSClaudio Fontana /* Trap if accessing an invalid page. */
272a3ef070eSClaudio Fontana mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, 1,
2730b5ad31dSPeter Maydell MMU_DATA_LOAD, GETPC());
274a3ef070eSClaudio Fontana
275a3ef070eSClaudio Fontana /* Load if page supports tags. */
276a3ef070eSClaudio Fontana if (mem) {
277a3ef070eSClaudio Fontana rtag = load_tag1(ptr, mem);
278a3ef070eSClaudio Fontana }
279a3ef070eSClaudio Fontana
280a3ef070eSClaudio Fontana return address_with_allocation_tag(xt, rtag);
281a3ef070eSClaudio Fontana }
282a3ef070eSClaudio Fontana
check_tag_aligned(CPUARMState * env,uint64_t ptr,uintptr_t ra)283a3ef070eSClaudio Fontana static void check_tag_aligned(CPUARMState *env, uint64_t ptr, uintptr_t ra)
284a3ef070eSClaudio Fontana {
285a3ef070eSClaudio Fontana if (unlikely(!QEMU_IS_ALIGNED(ptr, TAG_GRANULE))) {
286a3ef070eSClaudio Fontana arm_cpu_do_unaligned_access(env_cpu(env), ptr, MMU_DATA_STORE,
287b7770d72SRichard Henderson arm_env_mmu_index(env), ra);
288a3ef070eSClaudio Fontana g_assert_not_reached();
289a3ef070eSClaudio Fontana }
290a3ef070eSClaudio Fontana }
291a3ef070eSClaudio Fontana
292a3ef070eSClaudio Fontana /* For use in a non-parallel context, store to the given nibble. */
store_tag1(uint64_t ptr,uint8_t * mem,int tag)293*0c9b437cSGustavo Romero void store_tag1(uint64_t ptr, uint8_t *mem, int tag)
294a3ef070eSClaudio Fontana {
295a3ef070eSClaudio Fontana int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4;
296a3ef070eSClaudio Fontana *mem = deposit32(*mem, ofs, 4, tag);
297a3ef070eSClaudio Fontana }
298a3ef070eSClaudio Fontana
299a3ef070eSClaudio Fontana /* For use in a parallel context, atomically store to the given nibble. */
store_tag1_parallel(uint64_t ptr,uint8_t * mem,int tag)300a3ef070eSClaudio Fontana static void store_tag1_parallel(uint64_t ptr, uint8_t *mem, int tag)
301a3ef070eSClaudio Fontana {
302a3ef070eSClaudio Fontana int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4;
303a3ef070eSClaudio Fontana uint8_t old = qatomic_read(mem);
304a3ef070eSClaudio Fontana
305a3ef070eSClaudio Fontana while (1) {
306a3ef070eSClaudio Fontana uint8_t new = deposit32(old, ofs, 4, tag);
307a3ef070eSClaudio Fontana uint8_t cmp = qatomic_cmpxchg(mem, old, new);
308a3ef070eSClaudio Fontana if (likely(cmp == old)) {
309a3ef070eSClaudio Fontana return;
310a3ef070eSClaudio Fontana }
311a3ef070eSClaudio Fontana old = cmp;
312a3ef070eSClaudio Fontana }
313a3ef070eSClaudio Fontana }
314a3ef070eSClaudio Fontana
315a3ef070eSClaudio Fontana typedef void stg_store1(uint64_t, uint8_t *, int);
316a3ef070eSClaudio Fontana
do_stg(CPUARMState * env,uint64_t ptr,uint64_t xt,uintptr_t ra,stg_store1 store1)317a3ef070eSClaudio Fontana static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt,
318a3ef070eSClaudio Fontana uintptr_t ra, stg_store1 store1)
319a3ef070eSClaudio Fontana {
320b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
321a3ef070eSClaudio Fontana uint8_t *mem;
322a3ef070eSClaudio Fontana
323a3ef070eSClaudio Fontana check_tag_aligned(env, ptr, ra);
324a3ef070eSClaudio Fontana
325a3ef070eSClaudio Fontana /* Trap if accessing an invalid page. */
326a3ef070eSClaudio Fontana mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, TAG_GRANULE,
3270b5ad31dSPeter Maydell MMU_DATA_STORE, ra);
328a3ef070eSClaudio Fontana
329a3ef070eSClaudio Fontana /* Store if page supports tags. */
330a3ef070eSClaudio Fontana if (mem) {
331a3ef070eSClaudio Fontana store1(ptr, mem, allocation_tag_from_addr(xt));
332a3ef070eSClaudio Fontana }
333a3ef070eSClaudio Fontana }
334a3ef070eSClaudio Fontana
HELPER(stg)335a3ef070eSClaudio Fontana void HELPER(stg)(CPUARMState *env, uint64_t ptr, uint64_t xt)
336a3ef070eSClaudio Fontana {
337a3ef070eSClaudio Fontana do_stg(env, ptr, xt, GETPC(), store_tag1);
338a3ef070eSClaudio Fontana }
339a3ef070eSClaudio Fontana
HELPER(stg_parallel)340a3ef070eSClaudio Fontana void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt)
341a3ef070eSClaudio Fontana {
342a3ef070eSClaudio Fontana do_stg(env, ptr, xt, GETPC(), store_tag1_parallel);
343a3ef070eSClaudio Fontana }
344a3ef070eSClaudio Fontana
HELPER(stg_stub)345a3ef070eSClaudio Fontana void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr)
346a3ef070eSClaudio Fontana {
347b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
348a3ef070eSClaudio Fontana uintptr_t ra = GETPC();
349a3ef070eSClaudio Fontana
350a3ef070eSClaudio Fontana check_tag_aligned(env, ptr, ra);
351a3ef070eSClaudio Fontana probe_write(env, ptr, TAG_GRANULE, mmu_idx, ra);
352a3ef070eSClaudio Fontana }
353a3ef070eSClaudio Fontana
do_st2g(CPUARMState * env,uint64_t ptr,uint64_t xt,uintptr_t ra,stg_store1 store1)354a3ef070eSClaudio Fontana static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt,
355a3ef070eSClaudio Fontana uintptr_t ra, stg_store1 store1)
356a3ef070eSClaudio Fontana {
357b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
358a3ef070eSClaudio Fontana int tag = allocation_tag_from_addr(xt);
359a3ef070eSClaudio Fontana uint8_t *mem1, *mem2;
360a3ef070eSClaudio Fontana
361a3ef070eSClaudio Fontana check_tag_aligned(env, ptr, ra);
362a3ef070eSClaudio Fontana
363a3ef070eSClaudio Fontana /*
364a3ef070eSClaudio Fontana * Trap if accessing an invalid page(s).
365a3ef070eSClaudio Fontana * This takes priority over !allocation_tag_access_enabled.
366a3ef070eSClaudio Fontana */
367a3ef070eSClaudio Fontana if (ptr & TAG_GRANULE) {
368a3ef070eSClaudio Fontana /* Two stores unaligned mod TAG_GRANULE*2 -- modify two bytes. */
369a3ef070eSClaudio Fontana mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE,
3700b5ad31dSPeter Maydell TAG_GRANULE, MMU_DATA_STORE, ra);
371a3ef070eSClaudio Fontana mem2 = allocation_tag_mem(env, mmu_idx, ptr + TAG_GRANULE,
372a3ef070eSClaudio Fontana MMU_DATA_STORE, TAG_GRANULE,
3730b5ad31dSPeter Maydell MMU_DATA_STORE, ra);
374a3ef070eSClaudio Fontana
375a3ef070eSClaudio Fontana /* Store if page(s) support tags. */
376a3ef070eSClaudio Fontana if (mem1) {
377a3ef070eSClaudio Fontana store1(TAG_GRANULE, mem1, tag);
378a3ef070eSClaudio Fontana }
379a3ef070eSClaudio Fontana if (mem2) {
380a3ef070eSClaudio Fontana store1(0, mem2, tag);
381a3ef070eSClaudio Fontana }
382a3ef070eSClaudio Fontana } else {
383a3ef070eSClaudio Fontana /* Two stores aligned mod TAG_GRANULE*2 -- modify one byte. */
384a3ef070eSClaudio Fontana mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE,
3850b5ad31dSPeter Maydell 2 * TAG_GRANULE, MMU_DATA_STORE, ra);
386a3ef070eSClaudio Fontana if (mem1) {
387a3ef070eSClaudio Fontana tag |= tag << 4;
388a3ef070eSClaudio Fontana qatomic_set(mem1, tag);
389a3ef070eSClaudio Fontana }
390a3ef070eSClaudio Fontana }
391a3ef070eSClaudio Fontana }
392a3ef070eSClaudio Fontana
HELPER(st2g)393a3ef070eSClaudio Fontana void HELPER(st2g)(CPUARMState *env, uint64_t ptr, uint64_t xt)
394a3ef070eSClaudio Fontana {
395a3ef070eSClaudio Fontana do_st2g(env, ptr, xt, GETPC(), store_tag1);
396a3ef070eSClaudio Fontana }
397a3ef070eSClaudio Fontana
HELPER(st2g_parallel)398a3ef070eSClaudio Fontana void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt)
399a3ef070eSClaudio Fontana {
400a3ef070eSClaudio Fontana do_st2g(env, ptr, xt, GETPC(), store_tag1_parallel);
401a3ef070eSClaudio Fontana }
402a3ef070eSClaudio Fontana
HELPER(st2g_stub)403a3ef070eSClaudio Fontana void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr)
404a3ef070eSClaudio Fontana {
405b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
406a3ef070eSClaudio Fontana uintptr_t ra = GETPC();
407a3ef070eSClaudio Fontana int in_page = -(ptr | TARGET_PAGE_MASK);
408a3ef070eSClaudio Fontana
409a3ef070eSClaudio Fontana check_tag_aligned(env, ptr, ra);
410a3ef070eSClaudio Fontana
411a3ef070eSClaudio Fontana if (likely(in_page >= 2 * TAG_GRANULE)) {
412a3ef070eSClaudio Fontana probe_write(env, ptr, 2 * TAG_GRANULE, mmu_idx, ra);
413a3ef070eSClaudio Fontana } else {
414a3ef070eSClaudio Fontana probe_write(env, ptr, TAG_GRANULE, mmu_idx, ra);
415a3ef070eSClaudio Fontana probe_write(env, ptr + TAG_GRANULE, TAG_GRANULE, mmu_idx, ra);
416a3ef070eSClaudio Fontana }
417a3ef070eSClaudio Fontana }
418a3ef070eSClaudio Fontana
HELPER(ldgm)419a3ef070eSClaudio Fontana uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr)
420a3ef070eSClaudio Fontana {
421b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
422a3ef070eSClaudio Fontana uintptr_t ra = GETPC();
423851ec6ebSRichard Henderson int gm_bs = env_archcpu(env)->gm_blocksize;
424851ec6ebSRichard Henderson int gm_bs_bytes = 4 << gm_bs;
425a3ef070eSClaudio Fontana void *tag_mem;
4267134cb07SRichard Henderson uint64_t ret;
4277134cb07SRichard Henderson int shift;
428a3ef070eSClaudio Fontana
429851ec6ebSRichard Henderson ptr = QEMU_ALIGN_DOWN(ptr, gm_bs_bytes);
430a3ef070eSClaudio Fontana
431a3ef070eSClaudio Fontana /* Trap if accessing an invalid page. */
432a3ef070eSClaudio Fontana tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD,
4330b5ad31dSPeter Maydell gm_bs_bytes, MMU_DATA_LOAD, ra);
434a3ef070eSClaudio Fontana
435a3ef070eSClaudio Fontana /* The tag is squashed to zero if the page does not support tags. */
436a3ef070eSClaudio Fontana if (!tag_mem) {
437a3ef070eSClaudio Fontana return 0;
438a3ef070eSClaudio Fontana }
439a3ef070eSClaudio Fontana
440a3ef070eSClaudio Fontana /*
441851ec6ebSRichard Henderson * The ordering of elements within the word corresponds to
4427134cb07SRichard Henderson * a little-endian operation. Computation of shift comes from
4437134cb07SRichard Henderson *
4447134cb07SRichard Henderson * index = address<LOG2_TAG_GRANULE+3:LOG2_TAG_GRANULE>
4457134cb07SRichard Henderson * data<index*4+3:index*4> = tag
4467134cb07SRichard Henderson *
4477134cb07SRichard Henderson * Because of the alignment of ptr above, BS=6 has shift=0.
4487134cb07SRichard Henderson * All memory operations are aligned. Defer support for BS=2,
4497134cb07SRichard Henderson * requiring insertion or extraction of a nibble, until we
4507134cb07SRichard Henderson * support a cpu that requires it.
451a3ef070eSClaudio Fontana */
452851ec6ebSRichard Henderson switch (gm_bs) {
4537134cb07SRichard Henderson case 3:
4547134cb07SRichard Henderson /* 32 bytes -> 2 tags -> 8 result bits */
4557134cb07SRichard Henderson ret = *(uint8_t *)tag_mem;
4567134cb07SRichard Henderson break;
4577134cb07SRichard Henderson case 4:
4587134cb07SRichard Henderson /* 64 bytes -> 4 tags -> 16 result bits */
4597134cb07SRichard Henderson ret = cpu_to_le16(*(uint16_t *)tag_mem);
4607134cb07SRichard Henderson break;
4617134cb07SRichard Henderson case 5:
4627134cb07SRichard Henderson /* 128 bytes -> 8 tags -> 32 result bits */
4637134cb07SRichard Henderson ret = cpu_to_le32(*(uint32_t *)tag_mem);
4647134cb07SRichard Henderson break;
465851ec6ebSRichard Henderson case 6:
466851ec6ebSRichard Henderson /* 256 bytes -> 16 tags -> 64 result bits */
4677134cb07SRichard Henderson return cpu_to_le64(*(uint64_t *)tag_mem);
468851ec6ebSRichard Henderson default:
4697134cb07SRichard Henderson /*
4707134cb07SRichard Henderson * CPU configured with unsupported/invalid gm blocksize.
4717134cb07SRichard Henderson * This is detected early in arm_cpu_realizefn.
4727134cb07SRichard Henderson */
473851ec6ebSRichard Henderson g_assert_not_reached();
474851ec6ebSRichard Henderson }
4757134cb07SRichard Henderson shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4;
4767134cb07SRichard Henderson return ret << shift;
477a3ef070eSClaudio Fontana }
478a3ef070eSClaudio Fontana
HELPER(stgm)479a3ef070eSClaudio Fontana void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val)
480a3ef070eSClaudio Fontana {
481b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
482a3ef070eSClaudio Fontana uintptr_t ra = GETPC();
483851ec6ebSRichard Henderson int gm_bs = env_archcpu(env)->gm_blocksize;
484851ec6ebSRichard Henderson int gm_bs_bytes = 4 << gm_bs;
485a3ef070eSClaudio Fontana void *tag_mem;
4867134cb07SRichard Henderson int shift;
487a3ef070eSClaudio Fontana
488851ec6ebSRichard Henderson ptr = QEMU_ALIGN_DOWN(ptr, gm_bs_bytes);
489a3ef070eSClaudio Fontana
490a3ef070eSClaudio Fontana /* Trap if accessing an invalid page. */
491a3ef070eSClaudio Fontana tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE,
4920b5ad31dSPeter Maydell gm_bs_bytes, MMU_DATA_LOAD, ra);
493a3ef070eSClaudio Fontana
494a3ef070eSClaudio Fontana /*
495a3ef070eSClaudio Fontana * Tag store only happens if the page support tags,
496a3ef070eSClaudio Fontana * and if the OS has enabled access to the tags.
497a3ef070eSClaudio Fontana */
498a3ef070eSClaudio Fontana if (!tag_mem) {
499a3ef070eSClaudio Fontana return;
500a3ef070eSClaudio Fontana }
501a3ef070eSClaudio Fontana
5027134cb07SRichard Henderson /* See LDGM for comments on BS and on shift. */
5037134cb07SRichard Henderson shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4;
5047134cb07SRichard Henderson val >>= shift;
505851ec6ebSRichard Henderson switch (gm_bs) {
5067134cb07SRichard Henderson case 3:
5077134cb07SRichard Henderson /* 32 bytes -> 2 tags -> 8 result bits */
5087134cb07SRichard Henderson *(uint8_t *)tag_mem = val;
5097134cb07SRichard Henderson break;
5107134cb07SRichard Henderson case 4:
5117134cb07SRichard Henderson /* 64 bytes -> 4 tags -> 16 result bits */
5127134cb07SRichard Henderson *(uint16_t *)tag_mem = cpu_to_le16(val);
5137134cb07SRichard Henderson break;
5147134cb07SRichard Henderson case 5:
5157134cb07SRichard Henderson /* 128 bytes -> 8 tags -> 32 result bits */
5167134cb07SRichard Henderson *(uint32_t *)tag_mem = cpu_to_le32(val);
5177134cb07SRichard Henderson break;
518851ec6ebSRichard Henderson case 6:
5197134cb07SRichard Henderson /* 256 bytes -> 16 tags -> 64 result bits */
5207134cb07SRichard Henderson *(uint64_t *)tag_mem = cpu_to_le64(val);
521851ec6ebSRichard Henderson break;
522851ec6ebSRichard Henderson default:
523851ec6ebSRichard Henderson /* cpu configured with unsupported gm blocksize. */
524851ec6ebSRichard Henderson g_assert_not_reached();
525851ec6ebSRichard Henderson }
526a3ef070eSClaudio Fontana }
527a3ef070eSClaudio Fontana
HELPER(stzgm_tags)528a3ef070eSClaudio Fontana void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val)
529a3ef070eSClaudio Fontana {
530a3ef070eSClaudio Fontana uintptr_t ra = GETPC();
531b7770d72SRichard Henderson int mmu_idx = arm_env_mmu_index(env);
532a3ef070eSClaudio Fontana int log2_dcz_bytes, log2_tag_bytes;
533a3ef070eSClaudio Fontana intptr_t dcz_bytes, tag_bytes;
534a3ef070eSClaudio Fontana uint8_t *mem;
535a3ef070eSClaudio Fontana
536a3ef070eSClaudio Fontana /*
537a3ef070eSClaudio Fontana * In arm_cpu_realizefn, we assert that dcz > LOG2_TAG_GRANULE+1,
538a3ef070eSClaudio Fontana * i.e. 32 bytes, which is an unreasonably small dcz anyway,
539a3ef070eSClaudio Fontana * to make sure that we can access one complete tag byte here.
540a3ef070eSClaudio Fontana */
541a3ef070eSClaudio Fontana log2_dcz_bytes = env_archcpu(env)->dcz_blocksize + 2;
542a3ef070eSClaudio Fontana log2_tag_bytes = log2_dcz_bytes - (LOG2_TAG_GRANULE + 1);
543a3ef070eSClaudio Fontana dcz_bytes = (intptr_t)1 << log2_dcz_bytes;
544a3ef070eSClaudio Fontana tag_bytes = (intptr_t)1 << log2_tag_bytes;
545a3ef070eSClaudio Fontana ptr &= -dcz_bytes;
546a3ef070eSClaudio Fontana
547a3ef070eSClaudio Fontana mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, dcz_bytes,
5480b5ad31dSPeter Maydell MMU_DATA_STORE, ra);
549a3ef070eSClaudio Fontana if (mem) {
550a3ef070eSClaudio Fontana int tag_pair = (val & 0xf) * 0x11;
551a3ef070eSClaudio Fontana memset(mem, tag_pair, tag_bytes);
552a3ef070eSClaudio Fontana }
553a3ef070eSClaudio Fontana }
554a3ef070eSClaudio Fontana
mte_sync_check_fail(CPUARMState * env,uint32_t desc,uint64_t dirty_ptr,uintptr_t ra)555a3ef070eSClaudio Fontana static void mte_sync_check_fail(CPUARMState *env, uint32_t desc,
556a3ef070eSClaudio Fontana uint64_t dirty_ptr, uintptr_t ra)
557a3ef070eSClaudio Fontana {
558a3ef070eSClaudio Fontana int is_write, syn;
559a3ef070eSClaudio Fontana
560a3ef070eSClaudio Fontana env->exception.vaddress = dirty_ptr;
561a3ef070eSClaudio Fontana
562a3ef070eSClaudio Fontana is_write = FIELD_EX32(desc, MTEDESC, WRITE);
563a3ef070eSClaudio Fontana syn = syn_data_abort_no_iss(arm_current_el(env) != 0, 0, 0, 0, 0, is_write,
564a3ef070eSClaudio Fontana 0x11);
565a3ef070eSClaudio Fontana raise_exception_ra(env, EXCP_DATA_ABORT, syn, exception_target_el(env), ra);
566a3ef070eSClaudio Fontana g_assert_not_reached();
567a3ef070eSClaudio Fontana }
568a3ef070eSClaudio Fontana
mte_async_check_fail(CPUARMState * env,uint64_t dirty_ptr,uintptr_t ra,ARMMMUIdx arm_mmu_idx,int el)569a3ef070eSClaudio Fontana static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr,
570a3ef070eSClaudio Fontana uintptr_t ra, ARMMMUIdx arm_mmu_idx, int el)
571a3ef070eSClaudio Fontana {
572a3ef070eSClaudio Fontana int select;
573a3ef070eSClaudio Fontana
574a3ef070eSClaudio Fontana if (regime_has_2_ranges(arm_mmu_idx)) {
575a3ef070eSClaudio Fontana select = extract64(dirty_ptr, 55, 1);
576a3ef070eSClaudio Fontana } else {
577a3ef070eSClaudio Fontana select = 0;
578a3ef070eSClaudio Fontana }
579a3ef070eSClaudio Fontana env->cp15.tfsr_el[el] |= 1 << select;
580a3ef070eSClaudio Fontana #ifdef CONFIG_USER_ONLY
581a3ef070eSClaudio Fontana /*
582a3ef070eSClaudio Fontana * Stand in for a timer irq, setting _TIF_MTE_ASYNC_FAULT,
583a3ef070eSClaudio Fontana * which then sends a SIGSEGV when the thread is next scheduled.
584a3ef070eSClaudio Fontana * This cpu will return to the main loop at the end of the TB,
585a3ef070eSClaudio Fontana * which is rather sooner than "normal". But the alternative
586a3ef070eSClaudio Fontana * is waiting until the next syscall.
587a3ef070eSClaudio Fontana */
588a3ef070eSClaudio Fontana qemu_cpu_kick(env_cpu(env));
589a3ef070eSClaudio Fontana #endif
590a3ef070eSClaudio Fontana }
591a3ef070eSClaudio Fontana
592a3ef070eSClaudio Fontana /* Record a tag check failure. */
mte_check_fail(CPUARMState * env,uint32_t desc,uint64_t dirty_ptr,uintptr_t ra)59381639989SPeter Maydell void mte_check_fail(CPUARMState *env, uint32_t desc,
594a3ef070eSClaudio Fontana uint64_t dirty_ptr, uintptr_t ra)
595a3ef070eSClaudio Fontana {
596a3ef070eSClaudio Fontana int mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
597a3ef070eSClaudio Fontana ARMMMUIdx arm_mmu_idx = core_to_aa64_mmu_idx(mmu_idx);
598a3ef070eSClaudio Fontana int el, reg_el, tcf;
599a3ef070eSClaudio Fontana uint64_t sctlr;
600a3ef070eSClaudio Fontana
601a3ef070eSClaudio Fontana reg_el = regime_el(env, arm_mmu_idx);
602a3ef070eSClaudio Fontana sctlr = env->cp15.sctlr_el[reg_el];
603a3ef070eSClaudio Fontana
604a3ef070eSClaudio Fontana switch (arm_mmu_idx) {
605a3ef070eSClaudio Fontana case ARMMMUIdx_E10_0:
606a3ef070eSClaudio Fontana case ARMMMUIdx_E20_0:
607a3ef070eSClaudio Fontana el = 0;
608a3ef070eSClaudio Fontana tcf = extract64(sctlr, 38, 2);
609a3ef070eSClaudio Fontana break;
610a3ef070eSClaudio Fontana default:
611a3ef070eSClaudio Fontana el = reg_el;
612a3ef070eSClaudio Fontana tcf = extract64(sctlr, 40, 2);
613a3ef070eSClaudio Fontana }
614a3ef070eSClaudio Fontana
615a3ef070eSClaudio Fontana switch (tcf) {
616a3ef070eSClaudio Fontana case 1:
617a3ef070eSClaudio Fontana /* Tag check fail causes a synchronous exception. */
618a3ef070eSClaudio Fontana mte_sync_check_fail(env, desc, dirty_ptr, ra);
619a3ef070eSClaudio Fontana break;
620a3ef070eSClaudio Fontana
621a3ef070eSClaudio Fontana case 0:
622a3ef070eSClaudio Fontana /*
623a3ef070eSClaudio Fontana * Tag check fail does not affect the PE.
624a3ef070eSClaudio Fontana * We eliminate this case by not setting MTE_ACTIVE
625a3ef070eSClaudio Fontana * in tb_flags, so that we never make this runtime call.
626a3ef070eSClaudio Fontana */
627a3ef070eSClaudio Fontana g_assert_not_reached();
628a3ef070eSClaudio Fontana
629a3ef070eSClaudio Fontana case 2:
630a3ef070eSClaudio Fontana /* Tag check fail causes asynchronous flag set. */
631a3ef070eSClaudio Fontana mte_async_check_fail(env, dirty_ptr, ra, arm_mmu_idx, el);
632a3ef070eSClaudio Fontana break;
633a3ef070eSClaudio Fontana
634a3ef070eSClaudio Fontana case 3:
635a3ef070eSClaudio Fontana /*
636a3ef070eSClaudio Fontana * Tag check fail causes asynchronous flag set for stores, or
637a3ef070eSClaudio Fontana * a synchronous exception for loads.
638a3ef070eSClaudio Fontana */
639a3ef070eSClaudio Fontana if (FIELD_EX32(desc, MTEDESC, WRITE)) {
640a3ef070eSClaudio Fontana mte_async_check_fail(env, dirty_ptr, ra, arm_mmu_idx, el);
641a3ef070eSClaudio Fontana } else {
642a3ef070eSClaudio Fontana mte_sync_check_fail(env, desc, dirty_ptr, ra);
643a3ef070eSClaudio Fontana }
644a3ef070eSClaudio Fontana break;
645a3ef070eSClaudio Fontana }
646a3ef070eSClaudio Fontana }
647a3ef070eSClaudio Fontana
648a3ef070eSClaudio Fontana /**
649a3ef070eSClaudio Fontana * checkN:
650a3ef070eSClaudio Fontana * @tag: tag memory to test
651a3ef070eSClaudio Fontana * @odd: true to begin testing at tags at odd nibble
652a3ef070eSClaudio Fontana * @cmp: the tag to compare against
653a3ef070eSClaudio Fontana * @count: number of tags to test
654a3ef070eSClaudio Fontana *
655a3ef070eSClaudio Fontana * Return the number of successful tests.
656a3ef070eSClaudio Fontana * Thus a return value < @count indicates a failure.
657a3ef070eSClaudio Fontana *
658a3ef070eSClaudio Fontana * A note about sizes: count is expected to be small.
659a3ef070eSClaudio Fontana *
660a3ef070eSClaudio Fontana * The most common use will be LDP/STP of two integer registers,
661a3ef070eSClaudio Fontana * which means 16 bytes of memory touching at most 2 tags, but
662a3ef070eSClaudio Fontana * often the access is aligned and thus just 1 tag.
663a3ef070eSClaudio Fontana *
664a3ef070eSClaudio Fontana * Using AdvSIMD LD/ST (multiple), one can access 64 bytes of memory,
665a3ef070eSClaudio Fontana * touching at most 5 tags. SVE LDR/STR (vector) with the default
666a3ef070eSClaudio Fontana * vector length is also 64 bytes; the maximum architectural length
667a3ef070eSClaudio Fontana * is 256 bytes touching at most 9 tags.
668a3ef070eSClaudio Fontana *
669a3ef070eSClaudio Fontana * The loop below uses 7 logical operations and 1 memory operation
670a3ef070eSClaudio Fontana * per tag pair. An implementation that loads an aligned word and
671a3ef070eSClaudio Fontana * uses masking to ignore adjacent tags requires 18 logical operations
672a3ef070eSClaudio Fontana * and thus does not begin to pay off until 6 tags.
673a3ef070eSClaudio Fontana * Which, according to the survey above, is unlikely to be common.
674a3ef070eSClaudio Fontana */
checkN(uint8_t * mem,int odd,int cmp,int count)675a3ef070eSClaudio Fontana static int checkN(uint8_t *mem, int odd, int cmp, int count)
676a3ef070eSClaudio Fontana {
677a3ef070eSClaudio Fontana int n = 0, diff;
678a3ef070eSClaudio Fontana
679a3ef070eSClaudio Fontana /* Replicate the test tag and compare. */
680a3ef070eSClaudio Fontana cmp *= 0x11;
681a3ef070eSClaudio Fontana diff = *mem++ ^ cmp;
682a3ef070eSClaudio Fontana
683a3ef070eSClaudio Fontana if (odd) {
684a3ef070eSClaudio Fontana goto start_odd;
685a3ef070eSClaudio Fontana }
686a3ef070eSClaudio Fontana
687a3ef070eSClaudio Fontana while (1) {
688a3ef070eSClaudio Fontana /* Test even tag. */
689a3ef070eSClaudio Fontana if (unlikely((diff) & 0x0f)) {
690a3ef070eSClaudio Fontana break;
691a3ef070eSClaudio Fontana }
692a3ef070eSClaudio Fontana if (++n == count) {
693a3ef070eSClaudio Fontana break;
694a3ef070eSClaudio Fontana }
695a3ef070eSClaudio Fontana
696a3ef070eSClaudio Fontana start_odd:
697a3ef070eSClaudio Fontana /* Test odd tag. */
698a3ef070eSClaudio Fontana if (unlikely((diff) & 0xf0)) {
699a3ef070eSClaudio Fontana break;
700a3ef070eSClaudio Fontana }
701a3ef070eSClaudio Fontana if (++n == count) {
702a3ef070eSClaudio Fontana break;
703a3ef070eSClaudio Fontana }
704a3ef070eSClaudio Fontana
705a3ef070eSClaudio Fontana diff = *mem++ ^ cmp;
706a3ef070eSClaudio Fontana }
707a3ef070eSClaudio Fontana return n;
708a3ef070eSClaudio Fontana }
709a3ef070eSClaudio Fontana
710a3ef070eSClaudio Fontana /**
71169c51dc3SPeter Maydell * checkNrev:
71269c51dc3SPeter Maydell * @tag: tag memory to test
71369c51dc3SPeter Maydell * @odd: true to begin testing at tags at odd nibble
71469c51dc3SPeter Maydell * @cmp: the tag to compare against
71569c51dc3SPeter Maydell * @count: number of tags to test
71669c51dc3SPeter Maydell *
71769c51dc3SPeter Maydell * Return the number of successful tests.
71869c51dc3SPeter Maydell * Thus a return value < @count indicates a failure.
71969c51dc3SPeter Maydell *
72069c51dc3SPeter Maydell * This is like checkN, but it runs backwards, checking the
72169c51dc3SPeter Maydell * tags starting with @tag and then the tags preceding it.
72269c51dc3SPeter Maydell * This is needed by the backwards-memory-copying operations.
72369c51dc3SPeter Maydell */
checkNrev(uint8_t * mem,int odd,int cmp,int count)72469c51dc3SPeter Maydell static int checkNrev(uint8_t *mem, int odd, int cmp, int count)
72569c51dc3SPeter Maydell {
72669c51dc3SPeter Maydell int n = 0, diff;
72769c51dc3SPeter Maydell
72869c51dc3SPeter Maydell /* Replicate the test tag and compare. */
72969c51dc3SPeter Maydell cmp *= 0x11;
73069c51dc3SPeter Maydell diff = *mem-- ^ cmp;
73169c51dc3SPeter Maydell
73269c51dc3SPeter Maydell if (!odd) {
73369c51dc3SPeter Maydell goto start_even;
73469c51dc3SPeter Maydell }
73569c51dc3SPeter Maydell
73669c51dc3SPeter Maydell while (1) {
73769c51dc3SPeter Maydell /* Test odd tag. */
73869c51dc3SPeter Maydell if (unlikely((diff) & 0xf0)) {
73969c51dc3SPeter Maydell break;
74069c51dc3SPeter Maydell }
74169c51dc3SPeter Maydell if (++n == count) {
74269c51dc3SPeter Maydell break;
74369c51dc3SPeter Maydell }
74469c51dc3SPeter Maydell
74569c51dc3SPeter Maydell start_even:
74669c51dc3SPeter Maydell /* Test even tag. */
74769c51dc3SPeter Maydell if (unlikely((diff) & 0x0f)) {
74869c51dc3SPeter Maydell break;
74969c51dc3SPeter Maydell }
75069c51dc3SPeter Maydell if (++n == count) {
75169c51dc3SPeter Maydell break;
75269c51dc3SPeter Maydell }
75369c51dc3SPeter Maydell
75469c51dc3SPeter Maydell diff = *mem-- ^ cmp;
75569c51dc3SPeter Maydell }
75669c51dc3SPeter Maydell return n;
75769c51dc3SPeter Maydell }
75869c51dc3SPeter Maydell
75969c51dc3SPeter Maydell /**
760a3ef070eSClaudio Fontana * mte_probe_int() - helper for mte_probe and mte_check
761a3ef070eSClaudio Fontana * @env: CPU environment
762a3ef070eSClaudio Fontana * @desc: MTEDESC descriptor
763a3ef070eSClaudio Fontana * @ptr: virtual address of the base of the access
764a3ef070eSClaudio Fontana * @fault: return virtual address of the first check failure
765a3ef070eSClaudio Fontana *
766a3ef070eSClaudio Fontana * Internal routine for both mte_probe and mte_check.
767a3ef070eSClaudio Fontana * Return zero on failure, filling in *fault.
768a3ef070eSClaudio Fontana * Return negative on trivial success for tbi disabled.
769a3ef070eSClaudio Fontana * Return positive on success with tbi enabled.
770a3ef070eSClaudio Fontana */
mte_probe_int(CPUARMState * env,uint32_t desc,uint64_t ptr,uintptr_t ra,uint64_t * fault)771a3ef070eSClaudio Fontana static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr,
772a3ef070eSClaudio Fontana uintptr_t ra, uint64_t *fault)
773a3ef070eSClaudio Fontana {
774a3ef070eSClaudio Fontana int mmu_idx, ptr_tag, bit55;
775a3ef070eSClaudio Fontana uint64_t ptr_last, prev_page, next_page;
776a3ef070eSClaudio Fontana uint64_t tag_first, tag_last;
7770b5ad31dSPeter Maydell uint32_t sizem1, tag_count, n, c;
778a3ef070eSClaudio Fontana uint8_t *mem1, *mem2;
779a3ef070eSClaudio Fontana MMUAccessType type;
780a3ef070eSClaudio Fontana
781a3ef070eSClaudio Fontana bit55 = extract64(ptr, 55, 1);
782a3ef070eSClaudio Fontana *fault = ptr;
783a3ef070eSClaudio Fontana
784a3ef070eSClaudio Fontana /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */
785a3ef070eSClaudio Fontana if (unlikely(!tbi_check(desc, bit55))) {
786a3ef070eSClaudio Fontana return -1;
787a3ef070eSClaudio Fontana }
788a3ef070eSClaudio Fontana
789a3ef070eSClaudio Fontana ptr_tag = allocation_tag_from_addr(ptr);
790a3ef070eSClaudio Fontana
791a3ef070eSClaudio Fontana if (tcma_check(desc, bit55, ptr_tag)) {
792a3ef070eSClaudio Fontana return 1;
793a3ef070eSClaudio Fontana }
794a3ef070eSClaudio Fontana
795a3ef070eSClaudio Fontana mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
796a3ef070eSClaudio Fontana type = FIELD_EX32(desc, MTEDESC, WRITE) ? MMU_DATA_STORE : MMU_DATA_LOAD;
797a3ef070eSClaudio Fontana sizem1 = FIELD_EX32(desc, MTEDESC, SIZEM1);
798a3ef070eSClaudio Fontana
799a3ef070eSClaudio Fontana /* Find the addr of the end of the access */
800a3ef070eSClaudio Fontana ptr_last = ptr + sizem1;
801a3ef070eSClaudio Fontana
802a3ef070eSClaudio Fontana /* Round the bounds to the tag granule, and compute the number of tags. */
803a3ef070eSClaudio Fontana tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE);
804a3ef070eSClaudio Fontana tag_last = QEMU_ALIGN_DOWN(ptr_last, TAG_GRANULE);
805a3ef070eSClaudio Fontana tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1;
806a3ef070eSClaudio Fontana
807a3ef070eSClaudio Fontana /* Locate the page boundaries. */
808a3ef070eSClaudio Fontana prev_page = ptr & TARGET_PAGE_MASK;
809a3ef070eSClaudio Fontana next_page = prev_page + TARGET_PAGE_SIZE;
810a3ef070eSClaudio Fontana
811a3ef070eSClaudio Fontana if (likely(tag_last - prev_page < TARGET_PAGE_SIZE)) {
812a3ef070eSClaudio Fontana /* Memory access stays on one page. */
813a3ef070eSClaudio Fontana mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, sizem1 + 1,
8140b5ad31dSPeter Maydell MMU_DATA_LOAD, ra);
815a3ef070eSClaudio Fontana if (!mem1) {
816a3ef070eSClaudio Fontana return 1;
817a3ef070eSClaudio Fontana }
818a3ef070eSClaudio Fontana /* Perform all of the comparisons. */
819a3ef070eSClaudio Fontana n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, tag_count);
820a3ef070eSClaudio Fontana } else {
821a3ef070eSClaudio Fontana /* Memory access crosses to next page. */
822a3ef070eSClaudio Fontana mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, next_page - ptr,
8230b5ad31dSPeter Maydell MMU_DATA_LOAD, ra);
824a3ef070eSClaudio Fontana
825a3ef070eSClaudio Fontana mem2 = allocation_tag_mem(env, mmu_idx, next_page, type,
826a3ef070eSClaudio Fontana ptr_last - next_page + 1,
8270b5ad31dSPeter Maydell MMU_DATA_LOAD, ra);
828a3ef070eSClaudio Fontana
829a3ef070eSClaudio Fontana /*
830a3ef070eSClaudio Fontana * Perform all of the comparisons.
831a3ef070eSClaudio Fontana * Note the possible but unlikely case of the operation spanning
832a3ef070eSClaudio Fontana * two pages that do not both have tagging enabled.
833a3ef070eSClaudio Fontana */
834a3ef070eSClaudio Fontana n = c = (next_page - tag_first) / TAG_GRANULE;
835a3ef070eSClaudio Fontana if (mem1) {
836a3ef070eSClaudio Fontana n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, c);
837a3ef070eSClaudio Fontana }
838a3ef070eSClaudio Fontana if (n == c) {
839a3ef070eSClaudio Fontana if (!mem2) {
840a3ef070eSClaudio Fontana return 1;
841a3ef070eSClaudio Fontana }
842a3ef070eSClaudio Fontana n += checkN(mem2, 0, ptr_tag, tag_count - c);
843a3ef070eSClaudio Fontana }
844a3ef070eSClaudio Fontana }
845a3ef070eSClaudio Fontana
846a3ef070eSClaudio Fontana if (likely(n == tag_count)) {
847a3ef070eSClaudio Fontana return 1;
848a3ef070eSClaudio Fontana }
849a3ef070eSClaudio Fontana
850a3ef070eSClaudio Fontana /*
851a3ef070eSClaudio Fontana * If we failed, we know which granule. For the first granule, the
852a3ef070eSClaudio Fontana * failure address is @ptr, the first byte accessed. Otherwise the
853a3ef070eSClaudio Fontana * failure address is the first byte of the nth granule.
854a3ef070eSClaudio Fontana */
855a3ef070eSClaudio Fontana if (n > 0) {
856a3ef070eSClaudio Fontana *fault = tag_first + n * TAG_GRANULE;
857a3ef070eSClaudio Fontana }
858a3ef070eSClaudio Fontana return 0;
859a3ef070eSClaudio Fontana }
860a3ef070eSClaudio Fontana
mte_check(CPUARMState * env,uint32_t desc,uint64_t ptr,uintptr_t ra)861a3ef070eSClaudio Fontana uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra)
862a3ef070eSClaudio Fontana {
863a3ef070eSClaudio Fontana uint64_t fault;
864a3ef070eSClaudio Fontana int ret = mte_probe_int(env, desc, ptr, ra, &fault);
865a3ef070eSClaudio Fontana
866a3ef070eSClaudio Fontana if (unlikely(ret == 0)) {
867a3ef070eSClaudio Fontana mte_check_fail(env, desc, fault, ra);
868a3ef070eSClaudio Fontana } else if (ret < 0) {
869a3ef070eSClaudio Fontana return ptr;
870a3ef070eSClaudio Fontana }
871a3ef070eSClaudio Fontana return useronly_clean_ptr(ptr);
872a3ef070eSClaudio Fontana }
873a3ef070eSClaudio Fontana
HELPER(mte_check)874a3ef070eSClaudio Fontana uint64_t HELPER(mte_check)(CPUARMState *env, uint32_t desc, uint64_t ptr)
875a3ef070eSClaudio Fontana {
876523da6b9SRichard Henderson /*
877523da6b9SRichard Henderson * R_XCHFJ: Alignment check not caused by memory type is priority 1,
878523da6b9SRichard Henderson * higher than any translation fault. When MTE is disabled, tcg
879523da6b9SRichard Henderson * performs the alignment check during the code generated for the
880523da6b9SRichard Henderson * memory access. With MTE enabled, we must check this here before
881523da6b9SRichard Henderson * raising any translation fault in allocation_tag_mem.
882523da6b9SRichard Henderson */
883523da6b9SRichard Henderson unsigned align = FIELD_EX32(desc, MTEDESC, ALIGN);
884523da6b9SRichard Henderson if (unlikely(align)) {
885523da6b9SRichard Henderson align = (1u << align) - 1;
886523da6b9SRichard Henderson if (unlikely(ptr & align)) {
887523da6b9SRichard Henderson int idx = FIELD_EX32(desc, MTEDESC, MIDX);
888523da6b9SRichard Henderson bool w = FIELD_EX32(desc, MTEDESC, WRITE);
889523da6b9SRichard Henderson MMUAccessType type = w ? MMU_DATA_STORE : MMU_DATA_LOAD;
890523da6b9SRichard Henderson arm_cpu_do_unaligned_access(env_cpu(env), ptr, type, idx, GETPC());
891523da6b9SRichard Henderson }
892523da6b9SRichard Henderson }
893523da6b9SRichard Henderson
894a3ef070eSClaudio Fontana return mte_check(env, desc, ptr, GETPC());
895a3ef070eSClaudio Fontana }
896a3ef070eSClaudio Fontana
897a3ef070eSClaudio Fontana /*
898a3ef070eSClaudio Fontana * No-fault version of mte_check, to be used by SVE for MemSingleNF.
899a3ef070eSClaudio Fontana * Returns false if the access is Checked and the check failed. This
900a3ef070eSClaudio Fontana * is only intended to probe the tag -- the validity of the page must
901a3ef070eSClaudio Fontana * be checked beforehand.
902a3ef070eSClaudio Fontana */
mte_probe(CPUARMState * env,uint32_t desc,uint64_t ptr)903a3ef070eSClaudio Fontana bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr)
904a3ef070eSClaudio Fontana {
905a3ef070eSClaudio Fontana uint64_t fault;
906a3ef070eSClaudio Fontana int ret = mte_probe_int(env, desc, ptr, 0, &fault);
907a3ef070eSClaudio Fontana
908a3ef070eSClaudio Fontana return ret != 0;
909a3ef070eSClaudio Fontana }
910a3ef070eSClaudio Fontana
911a3ef070eSClaudio Fontana /*
912a3ef070eSClaudio Fontana * Perform an MTE checked access for DC_ZVA.
913a3ef070eSClaudio Fontana */
HELPER(mte_check_zva)914a3ef070eSClaudio Fontana uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr)
915a3ef070eSClaudio Fontana {
916a3ef070eSClaudio Fontana uintptr_t ra = GETPC();
917a3ef070eSClaudio Fontana int log2_dcz_bytes, log2_tag_bytes;
918a3ef070eSClaudio Fontana int mmu_idx, bit55;
919a3ef070eSClaudio Fontana intptr_t dcz_bytes, tag_bytes, i;
920a3ef070eSClaudio Fontana void *mem;
921a3ef070eSClaudio Fontana uint64_t ptr_tag, mem_tag, align_ptr;
922a3ef070eSClaudio Fontana
923a3ef070eSClaudio Fontana bit55 = extract64(ptr, 55, 1);
924a3ef070eSClaudio Fontana
925a3ef070eSClaudio Fontana /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */
926a3ef070eSClaudio Fontana if (unlikely(!tbi_check(desc, bit55))) {
927a3ef070eSClaudio Fontana return ptr;
928a3ef070eSClaudio Fontana }
929a3ef070eSClaudio Fontana
930a3ef070eSClaudio Fontana ptr_tag = allocation_tag_from_addr(ptr);
931a3ef070eSClaudio Fontana
932a3ef070eSClaudio Fontana if (tcma_check(desc, bit55, ptr_tag)) {
933a3ef070eSClaudio Fontana goto done;
934a3ef070eSClaudio Fontana }
935a3ef070eSClaudio Fontana
936a3ef070eSClaudio Fontana /*
937a3ef070eSClaudio Fontana * In arm_cpu_realizefn, we asserted that dcz > LOG2_TAG_GRANULE+1,
938a3ef070eSClaudio Fontana * i.e. 32 bytes, which is an unreasonably small dcz anyway, to make
939a3ef070eSClaudio Fontana * sure that we can access one complete tag byte here.
940a3ef070eSClaudio Fontana */
941a3ef070eSClaudio Fontana log2_dcz_bytes = env_archcpu(env)->dcz_blocksize + 2;
942a3ef070eSClaudio Fontana log2_tag_bytes = log2_dcz_bytes - (LOG2_TAG_GRANULE + 1);
943a3ef070eSClaudio Fontana dcz_bytes = (intptr_t)1 << log2_dcz_bytes;
944a3ef070eSClaudio Fontana tag_bytes = (intptr_t)1 << log2_tag_bytes;
945a3ef070eSClaudio Fontana align_ptr = ptr & -dcz_bytes;
946a3ef070eSClaudio Fontana
947a3ef070eSClaudio Fontana /*
948a3ef070eSClaudio Fontana * Trap if accessing an invalid page. DC_ZVA requires that we supply
949a3ef070eSClaudio Fontana * the original pointer for an invalid page. But watchpoints require
950a3ef070eSClaudio Fontana * that we probe the actual space. So do both.
951a3ef070eSClaudio Fontana */
952a3ef070eSClaudio Fontana mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
953a3ef070eSClaudio Fontana (void) probe_write(env, ptr, 1, mmu_idx, ra);
954a3ef070eSClaudio Fontana mem = allocation_tag_mem(env, mmu_idx, align_ptr, MMU_DATA_STORE,
9550b5ad31dSPeter Maydell dcz_bytes, MMU_DATA_LOAD, ra);
956a3ef070eSClaudio Fontana if (!mem) {
957a3ef070eSClaudio Fontana goto done;
958a3ef070eSClaudio Fontana }
959a3ef070eSClaudio Fontana
960a3ef070eSClaudio Fontana /*
961a3ef070eSClaudio Fontana * Unlike the reasoning for checkN, DC_ZVA is always aligned, and thus
962a3ef070eSClaudio Fontana * it is quite easy to perform all of the comparisons at once without
963a3ef070eSClaudio Fontana * any extra masking.
964a3ef070eSClaudio Fontana *
965a3ef070eSClaudio Fontana * The most common zva block size is 64; some of the thunderx cpus use
966a3ef070eSClaudio Fontana * a block size of 128. For user-only, aarch64_max_initfn will set the
967a3ef070eSClaudio Fontana * block size to 512. Fill out the other cases for future-proofing.
968a3ef070eSClaudio Fontana *
969a3ef070eSClaudio Fontana * In order to be able to find the first miscompare later, we want the
970a3ef070eSClaudio Fontana * tag bytes to be in little-endian order.
971a3ef070eSClaudio Fontana */
972a3ef070eSClaudio Fontana switch (log2_tag_bytes) {
973a3ef070eSClaudio Fontana case 0: /* zva_blocksize 32 */
974a3ef070eSClaudio Fontana mem_tag = *(uint8_t *)mem;
975a3ef070eSClaudio Fontana ptr_tag *= 0x11u;
976a3ef070eSClaudio Fontana break;
977a3ef070eSClaudio Fontana case 1: /* zva_blocksize 64 */
978a3ef070eSClaudio Fontana mem_tag = cpu_to_le16(*(uint16_t *)mem);
979a3ef070eSClaudio Fontana ptr_tag *= 0x1111u;
980a3ef070eSClaudio Fontana break;
981a3ef070eSClaudio Fontana case 2: /* zva_blocksize 128 */
982a3ef070eSClaudio Fontana mem_tag = cpu_to_le32(*(uint32_t *)mem);
983a3ef070eSClaudio Fontana ptr_tag *= 0x11111111u;
984a3ef070eSClaudio Fontana break;
985a3ef070eSClaudio Fontana case 3: /* zva_blocksize 256 */
986a3ef070eSClaudio Fontana mem_tag = cpu_to_le64(*(uint64_t *)mem);
987a3ef070eSClaudio Fontana ptr_tag *= 0x1111111111111111ull;
988a3ef070eSClaudio Fontana break;
989a3ef070eSClaudio Fontana
990a3ef070eSClaudio Fontana default: /* zva_blocksize 512, 1024, 2048 */
991a3ef070eSClaudio Fontana ptr_tag *= 0x1111111111111111ull;
992a3ef070eSClaudio Fontana i = 0;
993a3ef070eSClaudio Fontana do {
994a3ef070eSClaudio Fontana mem_tag = cpu_to_le64(*(uint64_t *)(mem + i));
995a3ef070eSClaudio Fontana if (unlikely(mem_tag != ptr_tag)) {
996a3ef070eSClaudio Fontana goto fail;
997a3ef070eSClaudio Fontana }
998a3ef070eSClaudio Fontana i += 8;
999a3ef070eSClaudio Fontana align_ptr += 16 * TAG_GRANULE;
1000a3ef070eSClaudio Fontana } while (i < tag_bytes);
1001a3ef070eSClaudio Fontana goto done;
1002a3ef070eSClaudio Fontana }
1003a3ef070eSClaudio Fontana
1004a3ef070eSClaudio Fontana if (likely(mem_tag == ptr_tag)) {
1005a3ef070eSClaudio Fontana goto done;
1006a3ef070eSClaudio Fontana }
1007a3ef070eSClaudio Fontana
1008a3ef070eSClaudio Fontana fail:
1009a3ef070eSClaudio Fontana /* Locate the first nibble that differs. */
1010a3ef070eSClaudio Fontana i = ctz64(mem_tag ^ ptr_tag) >> 4;
1011a3ef070eSClaudio Fontana mte_check_fail(env, desc, align_ptr + i * TAG_GRANULE, ra);
1012a3ef070eSClaudio Fontana
1013a3ef070eSClaudio Fontana done:
1014a3ef070eSClaudio Fontana return useronly_clean_ptr(ptr);
1015a3ef070eSClaudio Fontana }
101681639989SPeter Maydell
mte_mops_probe(CPUARMState * env,uint64_t ptr,uint64_t size,uint32_t desc)101781639989SPeter Maydell uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size,
101881639989SPeter Maydell uint32_t desc)
101981639989SPeter Maydell {
102081639989SPeter Maydell int mmu_idx, tag_count;
102181639989SPeter Maydell uint64_t ptr_tag, tag_first, tag_last;
102281639989SPeter Maydell void *mem;
102381639989SPeter Maydell bool w = FIELD_EX32(desc, MTEDESC, WRITE);
102481639989SPeter Maydell uint32_t n;
102581639989SPeter Maydell
102681639989SPeter Maydell mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
102781639989SPeter Maydell /* True probe; this will never fault */
102881639989SPeter Maydell mem = allocation_tag_mem_probe(env, mmu_idx, ptr,
102981639989SPeter Maydell w ? MMU_DATA_STORE : MMU_DATA_LOAD,
103081639989SPeter Maydell size, MMU_DATA_LOAD, true, 0);
103181639989SPeter Maydell if (!mem) {
103281639989SPeter Maydell return size;
103381639989SPeter Maydell }
103481639989SPeter Maydell
103581639989SPeter Maydell /*
103681639989SPeter Maydell * TODO: checkN() is not designed for checks of the size we expect
103781639989SPeter Maydell * for FEAT_MOPS operations, so we should implement this differently.
103881639989SPeter Maydell * Maybe we should do something like
103981639989SPeter Maydell * if (region start and size are aligned nicely) {
104081639989SPeter Maydell * do direct loads of 64 tag bits at a time;
104181639989SPeter Maydell * } else {
104281639989SPeter Maydell * call checkN()
104381639989SPeter Maydell * }
104481639989SPeter Maydell */
104581639989SPeter Maydell /* Round the bounds to the tag granule, and compute the number of tags. */
104681639989SPeter Maydell ptr_tag = allocation_tag_from_addr(ptr);
104781639989SPeter Maydell tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE);
104881639989SPeter Maydell tag_last = QEMU_ALIGN_DOWN(ptr + size - 1, TAG_GRANULE);
104981639989SPeter Maydell tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1;
105081639989SPeter Maydell n = checkN(mem, ptr & TAG_GRANULE, ptr_tag, tag_count);
105181639989SPeter Maydell if (likely(n == tag_count)) {
105281639989SPeter Maydell return size;
105381639989SPeter Maydell }
105481639989SPeter Maydell
105581639989SPeter Maydell /*
105681639989SPeter Maydell * Failure; for the first granule, it's at @ptr. Otherwise
105781639989SPeter Maydell * it's at the first byte of the nth granule. Calculate how
105881639989SPeter Maydell * many bytes we can access without hitting that failure.
105981639989SPeter Maydell */
106081639989SPeter Maydell if (n == 0) {
106181639989SPeter Maydell return 0;
106281639989SPeter Maydell } else {
106381639989SPeter Maydell return n * TAG_GRANULE - (ptr - tag_first);
106481639989SPeter Maydell }
106581639989SPeter Maydell }
10666087df57SPeter Maydell
mte_mops_probe_rev(CPUARMState * env,uint64_t ptr,uint64_t size,uint32_t desc)106769c51dc3SPeter Maydell uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size,
106869c51dc3SPeter Maydell uint32_t desc)
106969c51dc3SPeter Maydell {
107069c51dc3SPeter Maydell int mmu_idx, tag_count;
107169c51dc3SPeter Maydell uint64_t ptr_tag, tag_first, tag_last;
107269c51dc3SPeter Maydell void *mem;
107369c51dc3SPeter Maydell bool w = FIELD_EX32(desc, MTEDESC, WRITE);
107469c51dc3SPeter Maydell uint32_t n;
107569c51dc3SPeter Maydell
107669c51dc3SPeter Maydell mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
10774d044472SPeter Maydell /*
10784d044472SPeter Maydell * True probe; this will never fault. Note that our caller passes
10794d044472SPeter Maydell * us a pointer to the end of the region, but allocation_tag_mem_probe()
10804d044472SPeter Maydell * wants a pointer to the start. Because we know we don't span a page
10814d044472SPeter Maydell * boundary and that allocation_tag_mem_probe() doesn't otherwise care
10824d044472SPeter Maydell * about the size, pass in a size of 1 byte. This is simpler than
10834d044472SPeter Maydell * adjusting the ptr to point to the start of the region and then having
10844d044472SPeter Maydell * to adjust the returned 'mem' to get the end of the tag memory.
10854d044472SPeter Maydell */
108669c51dc3SPeter Maydell mem = allocation_tag_mem_probe(env, mmu_idx, ptr,
108769c51dc3SPeter Maydell w ? MMU_DATA_STORE : MMU_DATA_LOAD,
10884d044472SPeter Maydell 1, MMU_DATA_LOAD, true, 0);
108969c51dc3SPeter Maydell if (!mem) {
109069c51dc3SPeter Maydell return size;
109169c51dc3SPeter Maydell }
109269c51dc3SPeter Maydell
109369c51dc3SPeter Maydell /*
109469c51dc3SPeter Maydell * TODO: checkNrev() is not designed for checks of the size we expect
109569c51dc3SPeter Maydell * for FEAT_MOPS operations, so we should implement this differently.
109669c51dc3SPeter Maydell * Maybe we should do something like
109769c51dc3SPeter Maydell * if (region start and size are aligned nicely) {
109869c51dc3SPeter Maydell * do direct loads of 64 tag bits at a time;
109969c51dc3SPeter Maydell * } else {
110069c51dc3SPeter Maydell * call checkN()
110169c51dc3SPeter Maydell * }
110269c51dc3SPeter Maydell */
110369c51dc3SPeter Maydell /* Round the bounds to the tag granule, and compute the number of tags. */
110469c51dc3SPeter Maydell ptr_tag = allocation_tag_from_addr(ptr);
110569c51dc3SPeter Maydell tag_first = QEMU_ALIGN_DOWN(ptr - (size - 1), TAG_GRANULE);
110669c51dc3SPeter Maydell tag_last = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE);
110769c51dc3SPeter Maydell tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1;
110869c51dc3SPeter Maydell n = checkNrev(mem, ptr & TAG_GRANULE, ptr_tag, tag_count);
110969c51dc3SPeter Maydell if (likely(n == tag_count)) {
111069c51dc3SPeter Maydell return size;
111169c51dc3SPeter Maydell }
111269c51dc3SPeter Maydell
111369c51dc3SPeter Maydell /*
111469c51dc3SPeter Maydell * Failure; for the first granule, it's at @ptr. Otherwise
111569c51dc3SPeter Maydell * it's at the last byte of the nth granule. Calculate how
111669c51dc3SPeter Maydell * many bytes we can access without hitting that failure.
111769c51dc3SPeter Maydell */
111869c51dc3SPeter Maydell if (n == 0) {
111969c51dc3SPeter Maydell return 0;
112069c51dc3SPeter Maydell } else {
112169c51dc3SPeter Maydell return (n - 1) * TAG_GRANULE + ((ptr + 1) - tag_last);
112269c51dc3SPeter Maydell }
112369c51dc3SPeter Maydell }
112469c51dc3SPeter Maydell
mte_mops_set_tags(CPUARMState * env,uint64_t ptr,uint64_t size,uint32_t desc)11256087df57SPeter Maydell void mte_mops_set_tags(CPUARMState *env, uint64_t ptr, uint64_t size,
11266087df57SPeter Maydell uint32_t desc)
11276087df57SPeter Maydell {
11286087df57SPeter Maydell int mmu_idx, tag_count;
11296087df57SPeter Maydell uint64_t ptr_tag;
11306087df57SPeter Maydell void *mem;
11316087df57SPeter Maydell
11326087df57SPeter Maydell if (!desc) {
11336087df57SPeter Maydell /* Tags not actually enabled */
11346087df57SPeter Maydell return;
11356087df57SPeter Maydell }
11366087df57SPeter Maydell
11376087df57SPeter Maydell mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
11386087df57SPeter Maydell /* True probe: this will never fault */
11396087df57SPeter Maydell mem = allocation_tag_mem_probe(env, mmu_idx, ptr, MMU_DATA_STORE, size,
11406087df57SPeter Maydell MMU_DATA_STORE, true, 0);
11416087df57SPeter Maydell if (!mem) {
11426087df57SPeter Maydell return;
11436087df57SPeter Maydell }
11446087df57SPeter Maydell
11456087df57SPeter Maydell /*
11466087df57SPeter Maydell * We know that ptr and size are both TAG_GRANULE aligned; store
11476087df57SPeter Maydell * the tag from the pointer value into the tag memory.
11486087df57SPeter Maydell */
11496087df57SPeter Maydell ptr_tag = allocation_tag_from_addr(ptr);
11506087df57SPeter Maydell tag_count = size / TAG_GRANULE;
11516087df57SPeter Maydell if (ptr & TAG_GRANULE) {
11526087df57SPeter Maydell /* Not 2*TAG_GRANULE-aligned: store tag to first nibble */
11536087df57SPeter Maydell store_tag1_parallel(TAG_GRANULE, mem, ptr_tag);
11546087df57SPeter Maydell mem++;
11556087df57SPeter Maydell tag_count--;
11566087df57SPeter Maydell }
11576087df57SPeter Maydell memset(mem, ptr_tag | (ptr_tag << 4), tag_count / 2);
11586087df57SPeter Maydell if (tag_count & 1) {
11596087df57SPeter Maydell /* Final trailing unaligned nibble */
11606087df57SPeter Maydell mem += tag_count / 2;
11616087df57SPeter Maydell store_tag1_parallel(0, mem, ptr_tag);
11626087df57SPeter Maydell }
11636087df57SPeter Maydell }
1164