xref: /kernel/linux/linux-6.6/arch/arm64/lib/mte.S (revision 62306a36)
162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 ARM Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/linkage.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <asm/asm-uaccess.h>
862306a36Sopenharmony_ci#include <asm/assembler.h>
962306a36Sopenharmony_ci#include <asm/mte.h>
1062306a36Sopenharmony_ci#include <asm/page.h>
1162306a36Sopenharmony_ci#include <asm/sysreg.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	.arch	armv8.5-a+memtag
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * multitag_transfer_size - set \reg to the block size that is accessed by the
1762306a36Sopenharmony_ci * LDGM/STGM instructions.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci	.macro	multitag_transfer_size, reg, tmp
2062306a36Sopenharmony_ci	mrs_s	\reg, SYS_GMID_EL1
2162306a36Sopenharmony_ci	ubfx	\reg, \reg, #GMID_EL1_BS_SHIFT, #GMID_EL1_BS_WIDTH
2262306a36Sopenharmony_ci	mov	\tmp, #4
2362306a36Sopenharmony_ci	lsl	\reg, \tmp, \reg
2462306a36Sopenharmony_ci	.endm
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * Clear the tags in a page
2862306a36Sopenharmony_ci *   x0 - address of the page to be cleared
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ciSYM_FUNC_START(mte_clear_page_tags)
3162306a36Sopenharmony_ci	multitag_transfer_size x1, x2
3262306a36Sopenharmony_ci1:	stgm	xzr, [x0]
3362306a36Sopenharmony_ci	add	x0, x0, x1
3462306a36Sopenharmony_ci	tst	x0, #(PAGE_SIZE - 1)
3562306a36Sopenharmony_ci	b.ne	1b
3662306a36Sopenharmony_ci	ret
3762306a36Sopenharmony_ciSYM_FUNC_END(mte_clear_page_tags)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * Zero the page and tags at the same time
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Parameters:
4362306a36Sopenharmony_ci *	x0 - address to the beginning of the page
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ciSYM_FUNC_START(mte_zero_clear_page_tags)
4662306a36Sopenharmony_ci	and	x0, x0, #(1 << MTE_TAG_SHIFT) - 1	// clear the tag
4762306a36Sopenharmony_ci	mrs	x1, dczid_el0
4862306a36Sopenharmony_ci	tbnz	x1, #4, 2f	// Branch if DC GZVA is prohibited
4962306a36Sopenharmony_ci	and	w1, w1, #0xf
5062306a36Sopenharmony_ci	mov	x2, #4
5162306a36Sopenharmony_ci	lsl	x1, x2, x1
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci1:	dc	gzva, x0
5462306a36Sopenharmony_ci	add	x0, x0, x1
5562306a36Sopenharmony_ci	tst	x0, #(PAGE_SIZE - 1)
5662306a36Sopenharmony_ci	b.ne	1b
5762306a36Sopenharmony_ci	ret
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci2:	stz2g	x0, [x0], #(MTE_GRANULE_SIZE * 2)
6062306a36Sopenharmony_ci	tst	x0, #(PAGE_SIZE - 1)
6162306a36Sopenharmony_ci	b.ne	2b
6262306a36Sopenharmony_ci	ret
6362306a36Sopenharmony_ciSYM_FUNC_END(mte_zero_clear_page_tags)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * Copy the tags from the source page to the destination one
6762306a36Sopenharmony_ci *   x0 - address of the destination page
6862306a36Sopenharmony_ci *   x1 - address of the source page
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ciSYM_FUNC_START(mte_copy_page_tags)
7162306a36Sopenharmony_ci	mov	x2, x0
7262306a36Sopenharmony_ci	mov	x3, x1
7362306a36Sopenharmony_ci	multitag_transfer_size x5, x6
7462306a36Sopenharmony_ci1:	ldgm	x4, [x3]
7562306a36Sopenharmony_ci	stgm	x4, [x2]
7662306a36Sopenharmony_ci	add	x2, x2, x5
7762306a36Sopenharmony_ci	add	x3, x3, x5
7862306a36Sopenharmony_ci	tst	x2, #(PAGE_SIZE - 1)
7962306a36Sopenharmony_ci	b.ne	1b
8062306a36Sopenharmony_ci	ret
8162306a36Sopenharmony_ciSYM_FUNC_END(mte_copy_page_tags)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Read tags from a user buffer (one tag per byte) and set the corresponding
8562306a36Sopenharmony_ci * tags at the given kernel address. Used by PTRACE_POKEMTETAGS.
8662306a36Sopenharmony_ci *   x0 - kernel address (to)
8762306a36Sopenharmony_ci *   x1 - user buffer (from)
8862306a36Sopenharmony_ci *   x2 - number of tags/bytes (n)
8962306a36Sopenharmony_ci * Returns:
9062306a36Sopenharmony_ci *   x0 - number of tags read/set
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciSYM_FUNC_START(mte_copy_tags_from_user)
9362306a36Sopenharmony_ci	mov	x3, x1
9462306a36Sopenharmony_ci	cbz	x2, 2f
9562306a36Sopenharmony_ci1:
9662306a36Sopenharmony_ciUSER(2f, ldtrb	w4, [x1])
9762306a36Sopenharmony_ci	lsl	x4, x4, #MTE_TAG_SHIFT
9862306a36Sopenharmony_ci	stg	x4, [x0], #MTE_GRANULE_SIZE
9962306a36Sopenharmony_ci	add	x1, x1, #1
10062306a36Sopenharmony_ci	subs	x2, x2, #1
10162306a36Sopenharmony_ci	b.ne	1b
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	// exception handling and function return
10462306a36Sopenharmony_ci2:	sub	x0, x1, x3		// update the number of tags set
10562306a36Sopenharmony_ci	ret
10662306a36Sopenharmony_ciSYM_FUNC_END(mte_copy_tags_from_user)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Get the tags from a kernel address range and write the tag values to the
11062306a36Sopenharmony_ci * given user buffer (one tag per byte). Used by PTRACE_PEEKMTETAGS.
11162306a36Sopenharmony_ci *   x0 - user buffer (to)
11262306a36Sopenharmony_ci *   x1 - kernel address (from)
11362306a36Sopenharmony_ci *   x2 - number of tags/bytes (n)
11462306a36Sopenharmony_ci * Returns:
11562306a36Sopenharmony_ci *   x0 - number of tags read/set
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_ciSYM_FUNC_START(mte_copy_tags_to_user)
11862306a36Sopenharmony_ci	mov	x3, x0
11962306a36Sopenharmony_ci	cbz	x2, 2f
12062306a36Sopenharmony_ci1:
12162306a36Sopenharmony_ci	ldg	x4, [x1]
12262306a36Sopenharmony_ci	ubfx	x4, x4, #MTE_TAG_SHIFT, #MTE_TAG_SIZE
12362306a36Sopenharmony_ciUSER(2f, sttrb	w4, [x0])
12462306a36Sopenharmony_ci	add	x0, x0, #1
12562306a36Sopenharmony_ci	add	x1, x1, #MTE_GRANULE_SIZE
12662306a36Sopenharmony_ci	subs	x2, x2, #1
12762306a36Sopenharmony_ci	b.ne	1b
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	// exception handling and function return
13062306a36Sopenharmony_ci2:	sub	x0, x0, x3		// update the number of tags copied
13162306a36Sopenharmony_ci	ret
13262306a36Sopenharmony_ciSYM_FUNC_END(mte_copy_tags_to_user)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * Save the tags in a page
13662306a36Sopenharmony_ci *   x0 - page address
13762306a36Sopenharmony_ci *   x1 - tag storage, MTE_PAGE_TAG_STORAGE bytes
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ciSYM_FUNC_START(mte_save_page_tags)
14062306a36Sopenharmony_ci	multitag_transfer_size x7, x5
14162306a36Sopenharmony_ci1:
14262306a36Sopenharmony_ci	mov	x2, #0
14362306a36Sopenharmony_ci2:
14462306a36Sopenharmony_ci	ldgm	x5, [x0]
14562306a36Sopenharmony_ci	orr	x2, x2, x5
14662306a36Sopenharmony_ci	add	x0, x0, x7
14762306a36Sopenharmony_ci	tst	x0, #0xFF		// 16 tag values fit in a register,
14862306a36Sopenharmony_ci	b.ne	2b			// which is 16*16=256 bytes
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	str	x2, [x1], #8
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	tst	x0, #(PAGE_SIZE - 1)
15362306a36Sopenharmony_ci	b.ne	1b
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret
15662306a36Sopenharmony_ciSYM_FUNC_END(mte_save_page_tags)
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * Restore the tags in a page
16062306a36Sopenharmony_ci *   x0 - page address
16162306a36Sopenharmony_ci *   x1 - tag storage, MTE_PAGE_TAG_STORAGE bytes
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ciSYM_FUNC_START(mte_restore_page_tags)
16462306a36Sopenharmony_ci	multitag_transfer_size x7, x5
16562306a36Sopenharmony_ci1:
16662306a36Sopenharmony_ci	ldr	x2, [x1], #8
16762306a36Sopenharmony_ci2:
16862306a36Sopenharmony_ci	stgm	x2, [x0]
16962306a36Sopenharmony_ci	add	x0, x0, x7
17062306a36Sopenharmony_ci	tst	x0, #0xFF
17162306a36Sopenharmony_ci	b.ne	2b
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	tst	x0, #(PAGE_SIZE - 1)
17462306a36Sopenharmony_ci	b.ne	1b
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ret
17762306a36Sopenharmony_ciSYM_FUNC_END(mte_restore_page_tags)
178