1*7a338472SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2c6202ca7SKhalid Aziz /* adi_64.c: support for ADI (Application Data Integrity) feature on 3c6202ca7SKhalid Aziz * sparc m7 and newer processors. This feature is also known as 4c6202ca7SKhalid Aziz * SSM (Silicon Secured Memory). 5c6202ca7SKhalid Aziz * 6c6202ca7SKhalid Aziz * Copyright (C) 2016 Oracle and/or its affiliates. All rights reserved. 7c6202ca7SKhalid Aziz * Author: Khalid Aziz (khalid.aziz@oracle.com) 8c6202ca7SKhalid Aziz */ 9c6202ca7SKhalid Aziz #include <linux/init.h> 1074a04967SKhalid Aziz #include <linux/slab.h> 1174a04967SKhalid Aziz #include <linux/mm_types.h> 12c6202ca7SKhalid Aziz #include <asm/mdesc.h> 13c6202ca7SKhalid Aziz #include <asm/adi_64.h> 1474a04967SKhalid Aziz #include <asm/mmu_64.h> 1574a04967SKhalid Aziz #include <asm/pgtable_64.h> 1674a04967SKhalid Aziz 1774a04967SKhalid Aziz /* Each page of storage for ADI tags can accommodate tags for 128 1874a04967SKhalid Aziz * pages. When ADI enabled pages are being swapped out, it would be 1974a04967SKhalid Aziz * prudent to allocate at least enough tag storage space to accommodate 2074a04967SKhalid Aziz * SWAPFILE_CLUSTER number of pages. Allocate enough tag storage to 2174a04967SKhalid Aziz * store tags for four SWAPFILE_CLUSTER pages to reduce need for 2274a04967SKhalid Aziz * further allocations for same vma. 2374a04967SKhalid Aziz */ 2474a04967SKhalid Aziz #define TAG_STORAGE_PAGES 8 25c6202ca7SKhalid Aziz 26c6202ca7SKhalid Aziz struct adi_config adi_state; 2774a04967SKhalid Aziz EXPORT_SYMBOL(adi_state); 28c6202ca7SKhalid Aziz 29c6202ca7SKhalid Aziz /* mdesc_adi_init() : Parse machine description provided by the 30c6202ca7SKhalid Aziz * hypervisor to detect ADI capabilities 31c6202ca7SKhalid Aziz * 32c6202ca7SKhalid Aziz * Hypervisor reports ADI capabilities of platform in "hwcap-list" property 33c6202ca7SKhalid Aziz * for "cpu" node. If the platform supports ADI, "hwcap-list" property 34c6202ca7SKhalid Aziz * contains the keyword "adp". If the platform supports ADI, "platform" 35c6202ca7SKhalid Aziz * node will contain "adp-blksz", "adp-nbits" and "ue-on-adp" properties 36c6202ca7SKhalid Aziz * to describe the ADI capabilities. 37c6202ca7SKhalid Aziz */ 38c6202ca7SKhalid Aziz void __init mdesc_adi_init(void) 39c6202ca7SKhalid Aziz { 40c6202ca7SKhalid Aziz struct mdesc_handle *hp = mdesc_grab(); 41c6202ca7SKhalid Aziz const char *prop; 42c6202ca7SKhalid Aziz u64 pn, *val; 43c6202ca7SKhalid Aziz int len; 44c6202ca7SKhalid Aziz 45c6202ca7SKhalid Aziz if (!hp) 46c6202ca7SKhalid Aziz goto adi_not_found; 47c6202ca7SKhalid Aziz 48c6202ca7SKhalid Aziz pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "cpu"); 49c6202ca7SKhalid Aziz if (pn == MDESC_NODE_NULL) 50c6202ca7SKhalid Aziz goto adi_not_found; 51c6202ca7SKhalid Aziz 52c6202ca7SKhalid Aziz prop = mdesc_get_property(hp, pn, "hwcap-list", &len); 53c6202ca7SKhalid Aziz if (!prop) 54c6202ca7SKhalid Aziz goto adi_not_found; 55c6202ca7SKhalid Aziz 56c6202ca7SKhalid Aziz /* 57c6202ca7SKhalid Aziz * Look for "adp" keyword in hwcap-list which would indicate 58c6202ca7SKhalid Aziz * ADI support 59c6202ca7SKhalid Aziz */ 60c6202ca7SKhalid Aziz adi_state.enabled = false; 61c6202ca7SKhalid Aziz while (len) { 62c6202ca7SKhalid Aziz int plen; 63c6202ca7SKhalid Aziz 64c6202ca7SKhalid Aziz if (!strcmp(prop, "adp")) { 65c6202ca7SKhalid Aziz adi_state.enabled = true; 66c6202ca7SKhalid Aziz break; 67c6202ca7SKhalid Aziz } 68c6202ca7SKhalid Aziz 69c6202ca7SKhalid Aziz plen = strlen(prop) + 1; 70c6202ca7SKhalid Aziz prop += plen; 71c6202ca7SKhalid Aziz len -= plen; 72c6202ca7SKhalid Aziz } 73c6202ca7SKhalid Aziz 74c6202ca7SKhalid Aziz if (!adi_state.enabled) 75c6202ca7SKhalid Aziz goto adi_not_found; 76c6202ca7SKhalid Aziz 77c6202ca7SKhalid Aziz /* Find the ADI properties in "platform" node. If all ADI 78c6202ca7SKhalid Aziz * properties are not found, ADI support is incomplete and 79c6202ca7SKhalid Aziz * do not enable ADI in the kernel. 80c6202ca7SKhalid Aziz */ 81c6202ca7SKhalid Aziz pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform"); 82c6202ca7SKhalid Aziz if (pn == MDESC_NODE_NULL) 83c6202ca7SKhalid Aziz goto adi_not_found; 84c6202ca7SKhalid Aziz 85c6202ca7SKhalid Aziz val = (u64 *) mdesc_get_property(hp, pn, "adp-blksz", &len); 86c6202ca7SKhalid Aziz if (!val) 87c6202ca7SKhalid Aziz goto adi_not_found; 88c6202ca7SKhalid Aziz adi_state.caps.blksz = *val; 89c6202ca7SKhalid Aziz 90c6202ca7SKhalid Aziz val = (u64 *) mdesc_get_property(hp, pn, "adp-nbits", &len); 91c6202ca7SKhalid Aziz if (!val) 92c6202ca7SKhalid Aziz goto adi_not_found; 93c6202ca7SKhalid Aziz adi_state.caps.nbits = *val; 94c6202ca7SKhalid Aziz 95c6202ca7SKhalid Aziz val = (u64 *) mdesc_get_property(hp, pn, "ue-on-adp", &len); 96c6202ca7SKhalid Aziz if (!val) 97c6202ca7SKhalid Aziz goto adi_not_found; 98c6202ca7SKhalid Aziz adi_state.caps.ue_on_adi = *val; 99c6202ca7SKhalid Aziz 10074a04967SKhalid Aziz /* Some of the code to support swapping ADI tags is written 10174a04967SKhalid Aziz * assumption that two ADI tags can fit inside one byte. If 10274a04967SKhalid Aziz * this assumption is broken by a future architecture change, 10374a04967SKhalid Aziz * that code will have to be revisited. If that were to happen, 10474a04967SKhalid Aziz * disable ADI support so we do not get unpredictable results 10574a04967SKhalid Aziz * with programs trying to use ADI and their pages getting 10674a04967SKhalid Aziz * swapped out 10774a04967SKhalid Aziz */ 10874a04967SKhalid Aziz if (adi_state.caps.nbits > 4) { 10974a04967SKhalid Aziz pr_warn("WARNING: ADI tag size >4 on this platform. Disabling AADI support\n"); 11074a04967SKhalid Aziz adi_state.enabled = false; 11174a04967SKhalid Aziz } 11274a04967SKhalid Aziz 113c6202ca7SKhalid Aziz mdesc_release(hp); 114c6202ca7SKhalid Aziz return; 115c6202ca7SKhalid Aziz 116c6202ca7SKhalid Aziz adi_not_found: 117c6202ca7SKhalid Aziz adi_state.enabled = false; 118c6202ca7SKhalid Aziz adi_state.caps.blksz = 0; 119c6202ca7SKhalid Aziz adi_state.caps.nbits = 0; 120c6202ca7SKhalid Aziz if (hp) 121c6202ca7SKhalid Aziz mdesc_release(hp); 122c6202ca7SKhalid Aziz } 12374a04967SKhalid Aziz 12474a04967SKhalid Aziz tag_storage_desc_t *find_tag_store(struct mm_struct *mm, 12574a04967SKhalid Aziz struct vm_area_struct *vma, 12674a04967SKhalid Aziz unsigned long addr) 12774a04967SKhalid Aziz { 12874a04967SKhalid Aziz tag_storage_desc_t *tag_desc = NULL; 12974a04967SKhalid Aziz unsigned long i, max_desc, flags; 13074a04967SKhalid Aziz 13174a04967SKhalid Aziz /* Check if this vma already has tag storage descriptor 13274a04967SKhalid Aziz * allocated for it. 13374a04967SKhalid Aziz */ 13474a04967SKhalid Aziz max_desc = PAGE_SIZE/sizeof(tag_storage_desc_t); 13574a04967SKhalid Aziz if (mm->context.tag_store) { 13674a04967SKhalid Aziz tag_desc = mm->context.tag_store; 13774a04967SKhalid Aziz spin_lock_irqsave(&mm->context.tag_lock, flags); 13874a04967SKhalid Aziz for (i = 0; i < max_desc; i++) { 13974a04967SKhalid Aziz if ((addr >= tag_desc->start) && 14074a04967SKhalid Aziz ((addr + PAGE_SIZE - 1) <= tag_desc->end)) 14174a04967SKhalid Aziz break; 14274a04967SKhalid Aziz tag_desc++; 14374a04967SKhalid Aziz } 14474a04967SKhalid Aziz spin_unlock_irqrestore(&mm->context.tag_lock, flags); 14574a04967SKhalid Aziz 14674a04967SKhalid Aziz /* If no matching entries were found, this must be a 14774a04967SKhalid Aziz * freshly allocated page 14874a04967SKhalid Aziz */ 14974a04967SKhalid Aziz if (i >= max_desc) 15074a04967SKhalid Aziz tag_desc = NULL; 15174a04967SKhalid Aziz } 15274a04967SKhalid Aziz 15374a04967SKhalid Aziz return tag_desc; 15474a04967SKhalid Aziz } 15574a04967SKhalid Aziz 15674a04967SKhalid Aziz tag_storage_desc_t *alloc_tag_store(struct mm_struct *mm, 15774a04967SKhalid Aziz struct vm_area_struct *vma, 15874a04967SKhalid Aziz unsigned long addr) 15974a04967SKhalid Aziz { 16074a04967SKhalid Aziz unsigned char *tags; 16174a04967SKhalid Aziz unsigned long i, size, max_desc, flags; 16274a04967SKhalid Aziz tag_storage_desc_t *tag_desc, *open_desc; 16374a04967SKhalid Aziz unsigned long end_addr, hole_start, hole_end; 16474a04967SKhalid Aziz 16574a04967SKhalid Aziz max_desc = PAGE_SIZE/sizeof(tag_storage_desc_t); 16674a04967SKhalid Aziz open_desc = NULL; 16774a04967SKhalid Aziz hole_start = 0; 16874a04967SKhalid Aziz hole_end = ULONG_MAX; 16974a04967SKhalid Aziz end_addr = addr + PAGE_SIZE - 1; 17074a04967SKhalid Aziz 17174a04967SKhalid Aziz /* Check if this vma already has tag storage descriptor 17274a04967SKhalid Aziz * allocated for it. 17374a04967SKhalid Aziz */ 17474a04967SKhalid Aziz spin_lock_irqsave(&mm->context.tag_lock, flags); 17574a04967SKhalid Aziz if (mm->context.tag_store) { 17674a04967SKhalid Aziz tag_desc = mm->context.tag_store; 17774a04967SKhalid Aziz 17874a04967SKhalid Aziz /* Look for a matching entry for this address. While doing 17974a04967SKhalid Aziz * that, look for the first open slot as well and find 18074a04967SKhalid Aziz * the hole in already allocated range where this request 18174a04967SKhalid Aziz * will fit in. 18274a04967SKhalid Aziz */ 18374a04967SKhalid Aziz for (i = 0; i < max_desc; i++) { 18474a04967SKhalid Aziz if (tag_desc->tag_users == 0) { 18574a04967SKhalid Aziz if (open_desc == NULL) 18674a04967SKhalid Aziz open_desc = tag_desc; 18774a04967SKhalid Aziz } else { 18874a04967SKhalid Aziz if ((addr >= tag_desc->start) && 18974a04967SKhalid Aziz (tag_desc->end >= (addr + PAGE_SIZE - 1))) { 19074a04967SKhalid Aziz tag_desc->tag_users++; 19174a04967SKhalid Aziz goto out; 19274a04967SKhalid Aziz } 19374a04967SKhalid Aziz } 19474a04967SKhalid Aziz if ((tag_desc->start > end_addr) && 19574a04967SKhalid Aziz (tag_desc->start < hole_end)) 19674a04967SKhalid Aziz hole_end = tag_desc->start; 19774a04967SKhalid Aziz if ((tag_desc->end < addr) && 19874a04967SKhalid Aziz (tag_desc->end > hole_start)) 19974a04967SKhalid Aziz hole_start = tag_desc->end; 20074a04967SKhalid Aziz tag_desc++; 20174a04967SKhalid Aziz } 20274a04967SKhalid Aziz 20374a04967SKhalid Aziz } else { 20474a04967SKhalid Aziz size = sizeof(tag_storage_desc_t)*max_desc; 20574a04967SKhalid Aziz mm->context.tag_store = kzalloc(size, GFP_NOWAIT|__GFP_NOWARN); 20674a04967SKhalid Aziz if (mm->context.tag_store == NULL) { 20774a04967SKhalid Aziz tag_desc = NULL; 20874a04967SKhalid Aziz goto out; 20974a04967SKhalid Aziz } 21074a04967SKhalid Aziz tag_desc = mm->context.tag_store; 21174a04967SKhalid Aziz for (i = 0; i < max_desc; i++, tag_desc++) 21274a04967SKhalid Aziz tag_desc->tag_users = 0; 21374a04967SKhalid Aziz open_desc = mm->context.tag_store; 21474a04967SKhalid Aziz i = 0; 21574a04967SKhalid Aziz } 21674a04967SKhalid Aziz 21774a04967SKhalid Aziz /* Check if we ran out of tag storage descriptors */ 21874a04967SKhalid Aziz if (open_desc == NULL) { 21974a04967SKhalid Aziz tag_desc = NULL; 22074a04967SKhalid Aziz goto out; 22174a04967SKhalid Aziz } 22274a04967SKhalid Aziz 22374a04967SKhalid Aziz /* Mark this tag descriptor slot in use and then initialize it */ 22474a04967SKhalid Aziz tag_desc = open_desc; 22574a04967SKhalid Aziz tag_desc->tag_users = 1; 22674a04967SKhalid Aziz 22774a04967SKhalid Aziz /* Tag storage has not been allocated for this vma and space 22874a04967SKhalid Aziz * is available in tag storage descriptor. Since this page is 22974a04967SKhalid Aziz * being swapped out, there is high probability subsequent pages 23074a04967SKhalid Aziz * in the VMA will be swapped out as well. Allocate pages to 23174a04967SKhalid Aziz * store tags for as many pages in this vma as possible but not 23274a04967SKhalid Aziz * more than TAG_STORAGE_PAGES. Each byte in tag space holds 23374a04967SKhalid Aziz * two ADI tags since each ADI tag is 4 bits. Each ADI tag 23474a04967SKhalid Aziz * covers adi_blksize() worth of addresses. Check if the hole is 23574a04967SKhalid Aziz * big enough to accommodate full address range for using 23674a04967SKhalid Aziz * TAG_STORAGE_PAGES number of tag pages. 23774a04967SKhalid Aziz */ 23874a04967SKhalid Aziz size = TAG_STORAGE_PAGES * PAGE_SIZE; 23974a04967SKhalid Aziz end_addr = addr + (size*2*adi_blksize()) - 1; 24074a04967SKhalid Aziz /* Check for overflow. If overflow occurs, allocate only one page */ 24174a04967SKhalid Aziz if (end_addr < addr) { 24274a04967SKhalid Aziz size = PAGE_SIZE; 24374a04967SKhalid Aziz end_addr = addr + (size*2*adi_blksize()) - 1; 24474a04967SKhalid Aziz /* If overflow happens with the minimum tag storage 24574a04967SKhalid Aziz * allocation as well, adjust ending address for this 24674a04967SKhalid Aziz * tag storage. 24774a04967SKhalid Aziz */ 24874a04967SKhalid Aziz if (end_addr < addr) 24974a04967SKhalid Aziz end_addr = ULONG_MAX; 25074a04967SKhalid Aziz } 25174a04967SKhalid Aziz if (hole_end < end_addr) { 25274a04967SKhalid Aziz /* Available hole is too small on the upper end of 25374a04967SKhalid Aziz * address. Can we expand the range towards the lower 25474a04967SKhalid Aziz * address and maximize use of this slot? 25574a04967SKhalid Aziz */ 25674a04967SKhalid Aziz unsigned long tmp_addr; 25774a04967SKhalid Aziz 25874a04967SKhalid Aziz end_addr = hole_end - 1; 25974a04967SKhalid Aziz tmp_addr = end_addr - (size*2*adi_blksize()) + 1; 26074a04967SKhalid Aziz /* Check for underflow. If underflow occurs, allocate 26174a04967SKhalid Aziz * only one page for storing ADI tags 26274a04967SKhalid Aziz */ 26374a04967SKhalid Aziz if (tmp_addr > addr) { 26474a04967SKhalid Aziz size = PAGE_SIZE; 26574a04967SKhalid Aziz tmp_addr = end_addr - (size*2*adi_blksize()) - 1; 26674a04967SKhalid Aziz /* If underflow happens with the minimum tag storage 26774a04967SKhalid Aziz * allocation as well, adjust starting address for 26874a04967SKhalid Aziz * this tag storage. 26974a04967SKhalid Aziz */ 27074a04967SKhalid Aziz if (tmp_addr > addr) 27174a04967SKhalid Aziz tmp_addr = 0; 27274a04967SKhalid Aziz } 27374a04967SKhalid Aziz if (tmp_addr < hole_start) { 27474a04967SKhalid Aziz /* Available hole is restricted on lower address 27574a04967SKhalid Aziz * end as well 27674a04967SKhalid Aziz */ 27774a04967SKhalid Aziz tmp_addr = hole_start + 1; 27874a04967SKhalid Aziz } 27974a04967SKhalid Aziz addr = tmp_addr; 28074a04967SKhalid Aziz size = (end_addr + 1 - addr)/(2*adi_blksize()); 28174a04967SKhalid Aziz size = (size + (PAGE_SIZE-adi_blksize()))/PAGE_SIZE; 28274a04967SKhalid Aziz size = size * PAGE_SIZE; 28374a04967SKhalid Aziz } 28474a04967SKhalid Aziz tags = kzalloc(size, GFP_NOWAIT|__GFP_NOWARN); 28574a04967SKhalid Aziz if (tags == NULL) { 28674a04967SKhalid Aziz tag_desc->tag_users = 0; 28774a04967SKhalid Aziz tag_desc = NULL; 28874a04967SKhalid Aziz goto out; 28974a04967SKhalid Aziz } 29074a04967SKhalid Aziz tag_desc->start = addr; 29174a04967SKhalid Aziz tag_desc->tags = tags; 29274a04967SKhalid Aziz tag_desc->end = end_addr; 29374a04967SKhalid Aziz 29474a04967SKhalid Aziz out: 29574a04967SKhalid Aziz spin_unlock_irqrestore(&mm->context.tag_lock, flags); 29674a04967SKhalid Aziz return tag_desc; 29774a04967SKhalid Aziz } 29874a04967SKhalid Aziz 29974a04967SKhalid Aziz void del_tag_store(tag_storage_desc_t *tag_desc, struct mm_struct *mm) 30074a04967SKhalid Aziz { 30174a04967SKhalid Aziz unsigned long flags; 30274a04967SKhalid Aziz unsigned char *tags = NULL; 30374a04967SKhalid Aziz 30474a04967SKhalid Aziz spin_lock_irqsave(&mm->context.tag_lock, flags); 30574a04967SKhalid Aziz tag_desc->tag_users--; 30674a04967SKhalid Aziz if (tag_desc->tag_users == 0) { 30774a04967SKhalid Aziz tag_desc->start = tag_desc->end = 0; 30874a04967SKhalid Aziz /* Do not free up the tag storage space allocated 30974a04967SKhalid Aziz * by the first descriptor. This is persistent 31074a04967SKhalid Aziz * emergency tag storage space for the task. 31174a04967SKhalid Aziz */ 31274a04967SKhalid Aziz if (tag_desc != mm->context.tag_store) { 31374a04967SKhalid Aziz tags = tag_desc->tags; 31474a04967SKhalid Aziz tag_desc->tags = NULL; 31574a04967SKhalid Aziz } 31674a04967SKhalid Aziz } 31774a04967SKhalid Aziz spin_unlock_irqrestore(&mm->context.tag_lock, flags); 31874a04967SKhalid Aziz kfree(tags); 31974a04967SKhalid Aziz } 32074a04967SKhalid Aziz 32174a04967SKhalid Aziz #define tag_start(addr, tag_desc) \ 32274a04967SKhalid Aziz ((tag_desc)->tags + ((addr - (tag_desc)->start)/(2*adi_blksize()))) 32374a04967SKhalid Aziz 32474a04967SKhalid Aziz /* Retrieve any saved ADI tags for the page being swapped back in and 32574a04967SKhalid Aziz * restore these tags to the newly allocated physical page. 32674a04967SKhalid Aziz */ 32774a04967SKhalid Aziz void adi_restore_tags(struct mm_struct *mm, struct vm_area_struct *vma, 32874a04967SKhalid Aziz unsigned long addr, pte_t pte) 32974a04967SKhalid Aziz { 33074a04967SKhalid Aziz unsigned char *tag; 33174a04967SKhalid Aziz tag_storage_desc_t *tag_desc; 33274a04967SKhalid Aziz unsigned long paddr, tmp, version1, version2; 33374a04967SKhalid Aziz 33474a04967SKhalid Aziz /* Check if the swapped out page has an ADI version 33574a04967SKhalid Aziz * saved. If yes, restore version tag to the newly 33674a04967SKhalid Aziz * allocated page. 33774a04967SKhalid Aziz */ 33874a04967SKhalid Aziz tag_desc = find_tag_store(mm, vma, addr); 33974a04967SKhalid Aziz if (tag_desc == NULL) 34074a04967SKhalid Aziz return; 34174a04967SKhalid Aziz 34274a04967SKhalid Aziz tag = tag_start(addr, tag_desc); 34374a04967SKhalid Aziz paddr = pte_val(pte) & _PAGE_PADDR_4V; 34474a04967SKhalid Aziz for (tmp = paddr; tmp < (paddr+PAGE_SIZE); tmp += adi_blksize()) { 34574a04967SKhalid Aziz version1 = (*tag) >> 4; 34674a04967SKhalid Aziz version2 = (*tag) & 0x0f; 34774a04967SKhalid Aziz *tag++ = 0; 34874a04967SKhalid Aziz asm volatile("stxa %0, [%1] %2\n\t" 34974a04967SKhalid Aziz : 35074a04967SKhalid Aziz : "r" (version1), "r" (tmp), 35174a04967SKhalid Aziz "i" (ASI_MCD_REAL)); 35274a04967SKhalid Aziz tmp += adi_blksize(); 35374a04967SKhalid Aziz asm volatile("stxa %0, [%1] %2\n\t" 35474a04967SKhalid Aziz : 35574a04967SKhalid Aziz : "r" (version2), "r" (tmp), 35674a04967SKhalid Aziz "i" (ASI_MCD_REAL)); 35774a04967SKhalid Aziz } 35874a04967SKhalid Aziz asm volatile("membar #Sync\n\t"); 35974a04967SKhalid Aziz 36074a04967SKhalid Aziz /* Check and mark this tag space for release later if 36174a04967SKhalid Aziz * the swapped in page was the last user of tag space 36274a04967SKhalid Aziz */ 36374a04967SKhalid Aziz del_tag_store(tag_desc, mm); 36474a04967SKhalid Aziz } 36574a04967SKhalid Aziz 36674a04967SKhalid Aziz /* A page is about to be swapped out. Save any ADI tags associated with 36774a04967SKhalid Aziz * this physical page so they can be restored later when the page is swapped 36874a04967SKhalid Aziz * back in. 36974a04967SKhalid Aziz */ 37074a04967SKhalid Aziz int adi_save_tags(struct mm_struct *mm, struct vm_area_struct *vma, 37174a04967SKhalid Aziz unsigned long addr, pte_t oldpte) 37274a04967SKhalid Aziz { 37374a04967SKhalid Aziz unsigned char *tag; 37474a04967SKhalid Aziz tag_storage_desc_t *tag_desc; 37574a04967SKhalid Aziz unsigned long version1, version2, paddr, tmp; 37674a04967SKhalid Aziz 37774a04967SKhalid Aziz tag_desc = alloc_tag_store(mm, vma, addr); 37874a04967SKhalid Aziz if (tag_desc == NULL) 37974a04967SKhalid Aziz return -1; 38074a04967SKhalid Aziz 38174a04967SKhalid Aziz tag = tag_start(addr, tag_desc); 38274a04967SKhalid Aziz paddr = pte_val(oldpte) & _PAGE_PADDR_4V; 38374a04967SKhalid Aziz for (tmp = paddr; tmp < (paddr+PAGE_SIZE); tmp += adi_blksize()) { 38474a04967SKhalid Aziz asm volatile("ldxa [%1] %2, %0\n\t" 38574a04967SKhalid Aziz : "=r" (version1) 38674a04967SKhalid Aziz : "r" (tmp), "i" (ASI_MCD_REAL)); 38774a04967SKhalid Aziz tmp += adi_blksize(); 38874a04967SKhalid Aziz asm volatile("ldxa [%1] %2, %0\n\t" 38974a04967SKhalid Aziz : "=r" (version2) 39074a04967SKhalid Aziz : "r" (tmp), "i" (ASI_MCD_REAL)); 39174a04967SKhalid Aziz *tag = (version1 << 4) | version2; 39274a04967SKhalid Aziz tag++; 39374a04967SKhalid Aziz } 39474a04967SKhalid Aziz 39574a04967SKhalid Aziz return 0; 39674a04967SKhalid Aziz } 397