xref: /openbmc/qemu/target/arm/tcg/mte_helper.c (revision f2cb4026fccfe073f84a4b440e41d3ed0c3134f6)
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