xref: /linux/arch/arm64/lib/mte.S (revision a1c3be890440a1769ed6f822376a3e3ab0d42994)
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (C) 2020 ARM Ltd.
4 */
5#include <linux/linkage.h>
6
7#include <asm/asm-uaccess.h>
8#include <asm/assembler.h>
9#include <asm/mte.h>
10#include <asm/page.h>
11#include <asm/sysreg.h>
12
13	.arch	armv8.5-a+memtag
14
15/*
16 * multitag_transfer_size - set \reg to the block size that is accessed by the
17 * LDGM/STGM instructions.
18 */
19	.macro	multitag_transfer_size, reg, tmp
20	mrs_s	\reg, SYS_GMID_EL1
21	ubfx	\reg, \reg, #SYS_GMID_EL1_BS_SHIFT, #SYS_GMID_EL1_BS_SIZE
22	mov	\tmp, #4
23	lsl	\reg, \tmp, \reg
24	.endm
25
26/*
27 * Clear the tags in a page
28 *   x0 - address of the page to be cleared
29 */
30SYM_FUNC_START(mte_clear_page_tags)
31	multitag_transfer_size x1, x2
321:	stgm	xzr, [x0]
33	add	x0, x0, x1
34	tst	x0, #(PAGE_SIZE - 1)
35	b.ne	1b
36	ret
37SYM_FUNC_END(mte_clear_page_tags)
38
39/*
40 * Copy the tags from the source page to the destination one
41 *   x0 - address of the destination page
42 *   x1 - address of the source page
43 */
44SYM_FUNC_START(mte_copy_page_tags)
45	mov	x2, x0
46	mov	x3, x1
47	multitag_transfer_size x5, x6
481:	ldgm	x4, [x3]
49	stgm	x4, [x2]
50	add	x2, x2, x5
51	add	x3, x3, x5
52	tst	x2, #(PAGE_SIZE - 1)
53	b.ne	1b
54	ret
55SYM_FUNC_END(mte_copy_page_tags)
56
57/*
58 * Read tags from a user buffer (one tag per byte) and set the corresponding
59 * tags at the given kernel address. Used by PTRACE_POKEMTETAGS.
60 *   x0 - kernel address (to)
61 *   x1 - user buffer (from)
62 *   x2 - number of tags/bytes (n)
63 * Returns:
64 *   x0 - number of tags read/set
65 */
66SYM_FUNC_START(mte_copy_tags_from_user)
67	mov	x3, x1
68	cbz	x2, 2f
691:
70	user_ldst 2f, ldtrb, w4, x1, 0
71	lsl	x4, x4, #MTE_TAG_SHIFT
72	stg	x4, [x0], #MTE_GRANULE_SIZE
73	add	x1, x1, #1
74	subs	x2, x2, #1
75	b.ne	1b
76
77	// exception handling and function return
782:	sub	x0, x1, x3		// update the number of tags set
79	ret
80SYM_FUNC_END(mte_copy_tags_from_user)
81
82/*
83 * Get the tags from a kernel address range and write the tag values to the
84 * given user buffer (one tag per byte). Used by PTRACE_PEEKMTETAGS.
85 *   x0 - user buffer (to)
86 *   x1 - kernel address (from)
87 *   x2 - number of tags/bytes (n)
88 * Returns:
89 *   x0 - number of tags read/set
90 */
91SYM_FUNC_START(mte_copy_tags_to_user)
92	mov	x3, x0
93	cbz	x2, 2f
941:
95	ldg	x4, [x1]
96	ubfx	x4, x4, #MTE_TAG_SHIFT, #MTE_TAG_SIZE
97	user_ldst 2f, sttrb, w4, x0, 0
98	add	x0, x0, #1
99	add	x1, x1, #MTE_GRANULE_SIZE
100	subs	x2, x2, #1
101	b.ne	1b
102
103	// exception handling and function return
1042:	sub	x0, x0, x3		// update the number of tags copied
105	ret
106SYM_FUNC_END(mte_copy_tags_to_user)
107
108/*
109 * Save the tags in a page
110 *   x0 - page address
111 *   x1 - tag storage
112 */
113SYM_FUNC_START(mte_save_page_tags)
114	multitag_transfer_size x7, x5
1151:
116	mov	x2, #0
1172:
118	ldgm	x5, [x0]
119	orr	x2, x2, x5
120	add	x0, x0, x7
121	tst	x0, #0xFF		// 16 tag values fit in a register,
122	b.ne	2b			// which is 16*16=256 bytes
123
124	str	x2, [x1], #8
125
126	tst	x0, #(PAGE_SIZE - 1)
127	b.ne	1b
128
129	ret
130SYM_FUNC_END(mte_save_page_tags)
131
132/*
133 * Restore the tags in a page
134 *   x0 - page address
135 *   x1 - tag storage
136 */
137SYM_FUNC_START(mte_restore_page_tags)
138	multitag_transfer_size x7, x5
1391:
140	ldr	x2, [x1], #8
1412:
142	stgm	x2, [x0]
143	add	x0, x0, x7
144	tst	x0, #0xFF
145	b.ne	2b
146
147	tst	x0, #(PAGE_SIZE - 1)
148	b.ne	1b
149
150	ret
151SYM_FUNC_END(mte_restore_page_tags)
152