/* SPDX-License-Identifier: GPL-2.0-only */1/*2* Copyright (C) 2020 ARM Ltd.3*/4#include <linux/linkage.h>56#include <asm/asm-uaccess.h>7#include <asm/assembler.h>8#include <asm/mte.h>9#include <asm/page.h>10#include <asm/sysreg.h>1112.arch armv8.5-a+memtag1314/*15* multitag_transfer_size - set \reg to the block size that is accessed by the16* LDGM/STGM instructions.17*/18.macro multitag_transfer_size, reg, tmp19mrs_s \reg, SYS_GMID_EL120ubfx \reg, \reg, #GMID_EL1_BS_SHIFT, #GMID_EL1_BS_WIDTH21mov \tmp, #422lsl \reg, \tmp, \reg23.endm2425/*26* Clear the tags in a page27* x0 - address of the page to be cleared28*/29SYM_FUNC_START(mte_clear_page_tags)30multitag_transfer_size x1, x2311: stgm xzr, [x0]32add x0, x0, x133tst x0, #(PAGE_SIZE - 1)34b.ne 1b35ret36SYM_FUNC_END(mte_clear_page_tags)3738/*39* Zero the page and tags at the same time40*41* Parameters:42* x0 - address to the beginning of the page43*/44SYM_FUNC_START(mte_zero_clear_page_tags)45and x0, x0, #(1 << MTE_TAG_SHIFT) - 1 // clear the tag46mrs x1, dczid_el047tbnz x1, #4, 2f // Branch if DC GZVA is prohibited48and w1, w1, #0xf49mov x2, #450lsl x1, x2, x151521: dc gzva, x053add x0, x0, x154tst x0, #(PAGE_SIZE - 1)55b.ne 1b56ret57582: stz2g x0, [x0], #(MTE_GRANULE_SIZE * 2)59tst x0, #(PAGE_SIZE - 1)60b.ne 2b61ret62SYM_FUNC_END(mte_zero_clear_page_tags)6364/*65* Copy the tags from the source page to the destination one66* x0 - address of the destination page67* x1 - address of the source page68*/69SYM_FUNC_START(mte_copy_page_tags)70mov x2, x071mov x3, x172multitag_transfer_size x5, x6731: ldgm x4, [x3]74stgm x4, [x2]75add x2, x2, x576add x3, x3, x577tst x2, #(PAGE_SIZE - 1)78b.ne 1b79ret80SYM_FUNC_END(mte_copy_page_tags)8182/*83* Read tags from a user buffer (one tag per byte) and set the corresponding84* tags at the given kernel address. Used by PTRACE_POKEMTETAGS.85* x0 - kernel address (to)86* x1 - user buffer (from)87* x2 - number of tags/bytes (n)88* Returns:89* x0 - number of tags read/set90*/91SYM_FUNC_START(mte_copy_tags_from_user)92mov x3, x193cbz x2, 2f941:95USER(2f, ldtrb w4, [x1])96lsl x4, x4, #MTE_TAG_SHIFT97stg x4, [x0], #MTE_GRANULE_SIZE98add x1, x1, #199subs x2, x2, #1100b.ne 1b101102// exception handling and function return1032: sub x0, x1, x3 // update the number of tags set104ret105SYM_FUNC_END(mte_copy_tags_from_user)106107/*108* Get the tags from a kernel address range and write the tag values to the109* given user buffer (one tag per byte). Used by PTRACE_PEEKMTETAGS.110* x0 - user buffer (to)111* x1 - kernel address (from)112* x2 - number of tags/bytes (n)113* Returns:114* x0 - number of tags read/set115*/116SYM_FUNC_START(mte_copy_tags_to_user)117mov x3, x0118cbz x2, 2f1191:120ldg x4, [x1]121ubfx x4, x4, #MTE_TAG_SHIFT, #MTE_TAG_SIZE122USER(2f, sttrb w4, [x0])123add x0, x0, #1124add x1, x1, #MTE_GRANULE_SIZE125subs x2, x2, #1126b.ne 1b127128// exception handling and function return1292: sub x0, x0, x3 // update the number of tags copied130ret131SYM_FUNC_END(mte_copy_tags_to_user)132133/*134* Save the tags in a page135* x0 - page address136* x1 - tag storage, MTE_PAGE_TAG_STORAGE bytes137*/138SYM_FUNC_START(mte_save_page_tags)139multitag_transfer_size x7, x51401:141mov x2, #01422:143ldgm x5, [x0]144orr x2, x2, x5145add x0, x0, x7146tst x0, #0xFF // 16 tag values fit in a register,147b.ne 2b // which is 16*16=256 bytes148149str x2, [x1], #8150151tst x0, #(PAGE_SIZE - 1)152b.ne 1b153154ret155SYM_FUNC_END(mte_save_page_tags)156157/*158* Restore the tags in a page159* x0 - page address160* x1 - tag storage, MTE_PAGE_TAG_STORAGE bytes161*/162SYM_FUNC_START(mte_restore_page_tags)163multitag_transfer_size x7, x51641:165ldr x2, [x1], #81662:167stgm x2, [x0]168add x0, x0, x7169tst x0, #0xFF170b.ne 2b171172tst x0, #(PAGE_SIZE - 1)173b.ne 1b174175ret176SYM_FUNC_END(mte_restore_page_tags)177178179