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 153/* 154 * Assign allocation tags for a region of memory based on the pointer tag 155 * x0 - source pointer 156 * x1 - size 157 * 158 * Note: The address must be non-NULL and MTE_GRANULE_SIZE aligned and 159 * size must be non-zero and MTE_GRANULE_SIZE aligned. 160 */ 161SYM_FUNC_START(mte_assign_mem_tag_range) 1621: stg x0, [x0] 163 add x0, x0, #MTE_GRANULE_SIZE 164 subs x1, x1, #MTE_GRANULE_SIZE 165 b.gt 1b 166 ret 167SYM_FUNC_END(mte_assign_mem_tag_range) 168