|  | /* SPDX-License-Identifier: GPL-2.0-only */ | 
|  | /* | 
|  | * Copyright (C) 2020 ARM Ltd. | 
|  | */ | 
|  | #include <linux/linkage.h> | 
|  |  | 
|  | #include <asm/asm-uaccess.h> | 
|  | #include <asm/assembler.h> | 
|  | #include <asm/mte.h> | 
|  | #include <asm/page.h> | 
|  | #include <asm/sysreg.h> | 
|  |  | 
|  | .arch	armv8.5-a+memtag | 
|  |  | 
|  | /* | 
|  | * multitag_transfer_size - set \reg to the block size that is accessed by the | 
|  | * LDGM/STGM instructions. | 
|  | */ | 
|  | .macro	multitag_transfer_size, reg, tmp | 
|  | mrs_s	\reg, SYS_GMID_EL1 | 
|  | ubfx	\reg, \reg, #SYS_GMID_EL1_BS_SHIFT, #SYS_GMID_EL1_BS_SIZE | 
|  | mov	\tmp, #4 | 
|  | lsl	\reg, \tmp, \reg | 
|  | .endm | 
|  |  | 
|  | /* | 
|  | * Clear the tags in a page | 
|  | *   x0 - address of the page to be cleared | 
|  | */ | 
|  | SYM_FUNC_START(mte_clear_page_tags) | 
|  | multitag_transfer_size x1, x2 | 
|  | 1:	stgm	xzr, [x0] | 
|  | add	x0, x0, x1 | 
|  | tst	x0, #(PAGE_SIZE - 1) | 
|  | b.ne	1b | 
|  | ret | 
|  | SYM_FUNC_END(mte_clear_page_tags) | 
|  |  | 
|  | /* | 
|  | * Zero the page and tags at the same time | 
|  | * | 
|  | * Parameters: | 
|  | *	x0 - address to the beginning of the page | 
|  | */ | 
|  | SYM_FUNC_START(mte_zero_clear_page_tags) | 
|  | mrs	x1, dczid_el0 | 
|  | and	w1, w1, #0xf | 
|  | mov	x2, #4 | 
|  | lsl	x1, x2, x1 | 
|  | and	x0, x0, #(1 << MTE_TAG_SHIFT) - 1	// clear the tag | 
|  |  | 
|  | 1:	dc	gzva, x0 | 
|  | add	x0, x0, x1 | 
|  | tst	x0, #(PAGE_SIZE - 1) | 
|  | b.ne	1b | 
|  | ret | 
|  | SYM_FUNC_END(mte_zero_clear_page_tags) | 
|  |  | 
|  | /* | 
|  | * Copy the tags from the source page to the destination one | 
|  | *   x0 - address of the destination page | 
|  | *   x1 - address of the source page | 
|  | */ | 
|  | SYM_FUNC_START(mte_copy_page_tags) | 
|  | mov	x2, x0 | 
|  | mov	x3, x1 | 
|  | multitag_transfer_size x5, x6 | 
|  | 1:	ldgm	x4, [x3] | 
|  | stgm	x4, [x2] | 
|  | add	x2, x2, x5 | 
|  | add	x3, x3, x5 | 
|  | tst	x2, #(PAGE_SIZE - 1) | 
|  | b.ne	1b | 
|  | ret | 
|  | SYM_FUNC_END(mte_copy_page_tags) | 
|  |  | 
|  | /* | 
|  | * Read tags from a user buffer (one tag per byte) and set the corresponding | 
|  | * tags at the given kernel address. Used by PTRACE_POKEMTETAGS. | 
|  | *   x0 - kernel address (to) | 
|  | *   x1 - user buffer (from) | 
|  | *   x2 - number of tags/bytes (n) | 
|  | * Returns: | 
|  | *   x0 - number of tags read/set | 
|  | */ | 
|  | SYM_FUNC_START(mte_copy_tags_from_user) | 
|  | mov	x3, x1 | 
|  | cbz	x2, 2f | 
|  | 1: | 
|  | user_ldst 2f, ldtrb, w4, x1, 0 | 
|  | lsl	x4, x4, #MTE_TAG_SHIFT | 
|  | stg	x4, [x0], #MTE_GRANULE_SIZE | 
|  | add	x1, x1, #1 | 
|  | subs	x2, x2, #1 | 
|  | b.ne	1b | 
|  |  | 
|  | // exception handling and function return | 
|  | 2:	sub	x0, x1, x3		// update the number of tags set | 
|  | ret | 
|  | SYM_FUNC_END(mte_copy_tags_from_user) | 
|  |  | 
|  | /* | 
|  | * Get the tags from a kernel address range and write the tag values to the | 
|  | * given user buffer (one tag per byte). Used by PTRACE_PEEKMTETAGS. | 
|  | *   x0 - user buffer (to) | 
|  | *   x1 - kernel address (from) | 
|  | *   x2 - number of tags/bytes (n) | 
|  | * Returns: | 
|  | *   x0 - number of tags read/set | 
|  | */ | 
|  | SYM_FUNC_START(mte_copy_tags_to_user) | 
|  | mov	x3, x0 | 
|  | cbz	x2, 2f | 
|  | 1: | 
|  | ldg	x4, [x1] | 
|  | ubfx	x4, x4, #MTE_TAG_SHIFT, #MTE_TAG_SIZE | 
|  | user_ldst 2f, sttrb, w4, x0, 0 | 
|  | add	x0, x0, #1 | 
|  | add	x1, x1, #MTE_GRANULE_SIZE | 
|  | subs	x2, x2, #1 | 
|  | b.ne	1b | 
|  |  | 
|  | // exception handling and function return | 
|  | 2:	sub	x0, x0, x3		// update the number of tags copied | 
|  | ret | 
|  | SYM_FUNC_END(mte_copy_tags_to_user) | 
|  |  | 
|  | /* | 
|  | * Save the tags in a page | 
|  | *   x0 - page address | 
|  | *   x1 - tag storage | 
|  | */ | 
|  | SYM_FUNC_START(mte_save_page_tags) | 
|  | multitag_transfer_size x7, x5 | 
|  | 1: | 
|  | mov	x2, #0 | 
|  | 2: | 
|  | ldgm	x5, [x0] | 
|  | orr	x2, x2, x5 | 
|  | add	x0, x0, x7 | 
|  | tst	x0, #0xFF		// 16 tag values fit in a register, | 
|  | b.ne	2b			// which is 16*16=256 bytes | 
|  |  | 
|  | str	x2, [x1], #8 | 
|  |  | 
|  | tst	x0, #(PAGE_SIZE - 1) | 
|  | b.ne	1b | 
|  |  | 
|  | ret | 
|  | SYM_FUNC_END(mte_save_page_tags) | 
|  |  | 
|  | /* | 
|  | * Restore the tags in a page | 
|  | *   x0 - page address | 
|  | *   x1 - tag storage | 
|  | */ | 
|  | SYM_FUNC_START(mte_restore_page_tags) | 
|  | multitag_transfer_size x7, x5 | 
|  | 1: | 
|  | ldr	x2, [x1], #8 | 
|  | 2: | 
|  | stgm	x2, [x0] | 
|  | add	x0, x0, x7 | 
|  | tst	x0, #0xFF | 
|  | b.ne	2b | 
|  |  | 
|  | tst	x0, #(PAGE_SIZE - 1) | 
|  | b.ne	1b | 
|  |  | 
|  | ret | 
|  | SYM_FUNC_END(mte_restore_page_tags) |