From 53be87fe042b1130e585d2ce6560ffa1372a94fc Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Fri, 12 May 2017 13:31:42 -0700 Subject: [PATCH] proc: sparc64 ADI version tag debugging interface MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit To facilitate user space ADI debugging there needs to be a way for a debugger to get/set ADI version tags in a target process. This is accomplished with a new /proc//adi/tags interface. This new interface maps linearly to the address space of the target process at a ratio of 1:adi_blksz. A read (or write) of offset K in the file returns (or modifies) the ADI version tag stored in the cacheline containing address K * adi_blksz, encoded as 1 version per byte. Pseudocode example: unsigned char vers[2]; long long addr = 0x20000; fd = open(“/proc/pid/adi/tags”, O_RDONLY); addr /= adi_blksz(); rv = pread64(fd, &vers, 2, addr); /* * vers[0] gets version from address 0x20000, * vers[1] gets version from address 0x20000 + adi_blksz() */ Orabug: 26051178 Signed-off-by: Eric Snowberg Signed-off-by: Allen Pais Reviewed-by: Anthony Yznaga --- arch/sparc/include/asm/adi_64.h | 18 +++ fs/proc/Makefile | 1 + fs/proc/adi.c | 251 ++++++++++++++++++++++++++++++++ fs/proc/adi.h | 36 +++++ fs/proc/base.c | 7 + 5 files changed, 313 insertions(+) create mode 100644 fs/proc/adi.c create mode 100644 fs/proc/adi.h diff --git a/arch/sparc/include/asm/adi_64.h b/arch/sparc/include/asm/adi_64.h index 7fbe92a04495..fda1c5d5c1e8 100644 --- a/arch/sparc/include/asm/adi_64.h +++ b/arch/sparc/include/asm/adi_64.h @@ -8,6 +8,7 @@ #define __ASM_SPARC64_ADI_H #include +#include #ifndef __ASSEMBLY__ @@ -43,6 +44,23 @@ static inline unsigned long adi_nbits(void) return adi_state.caps.nbits; } +static inline unsigned char adi_get_version(void *addr) +{ + long version; + + asm volatile("ldxa [%1] %2, %0\n\t" + : "=r" (version) + : "r" (addr), "i" (ASI_MCD_PRIV_PRIMARY)); + return (unsigned char)version; +} + +static inline void adi_set_version(void *addr, int version) +{ + asm volatile("stxa %1, [%0] %2\n\t" + : + : "r" (addr), "r" (version), "i" (ASI_MCD_PRIV_PRIMARY)); +} + static inline unsigned long adi_normalize(long addr) { return addr << adi_nbits() >> adi_nbits(); diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 7151ea428041..0f23f0520188 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -30,3 +30,4 @@ proc-$(CONFIG_PROC_KCORE) += kcore.o proc-$(CONFIG_PROC_VMCORE) += vmcore.o proc-$(CONFIG_PRINTK) += kmsg.o proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o +proc-$(CONFIG_SPARC64) += adi.o diff --git a/fs/proc/adi.c b/fs/proc/adi.c new file mode 100644 index 000000000000..023edc2e7bd2 --- /dev/null +++ b/fs/proc/adi.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Softare Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adi.h" +#include "internal.h" + +#define MAX_TAG_VERSION ((1 << adi_nbits()) - 1) + +static inline void _membar_sync(void) +{ + asm volatile("membar #Sync\n\t"); +} + +static ssize_t _adi_tag_rw(char *vaddr, unsigned long voffset, ssize_t len, + char __user *buf, ssize_t pagesize, int write) +{ + ssize_t copied = 0; + unsigned char tag; + unsigned long adi_blksz = adi_blksize(); + + if (!adi_blksz) + return -ENOENT; + + while ((len > copied) && (voffset != pagesize)) { + if (write) { + if (copy_from_user(&tag, buf + copied, 1)) { + copied = -EFAULT; + break; + } + if (tag > MAX_TAG_VERSION) { + copied = -EINVAL; + break; + } + adi_set_version(vaddr + voffset, tag); + } else { + tag = adi_get_version(vaddr + voffset); + if (copy_to_user(buf + copied, &tag, 1)) { + copied = -EFAULT; + break; + } + } + + voffset += adi_blksz; + copied++; + } + return copied; +} + +static ssize_t adi_tag_rw(struct mm_struct *mm, struct task_struct *task, + char __user *buf, size_t count, + unsigned long long addr, int write) +{ + unsigned long voffset, start, page_sz = 0; + int res, locked, this_len = 0; + struct vm_area_struct *vma; + ssize_t copied = 0; + struct page *page; + char *vaddr; + + while (count) { + down_read(&mm->mmap_sem); + locked = 1; + vma = find_vma(mm, addr); + + if (!vma) { + up_read(&mm->mmap_sem); + copied = -EFAULT; + break; + } + + if (!(vma->vm_flags & VM_SPARC_ADI)) { + up_read(&mm->mmap_sem); + copied = -ENOMEM; + break; + } + + res = get_user_pages_locked(task, mm, addr, 1, write, 1, &page, + &locked); + + if (res != 1) { + if (!copied) + copied = -EIO; + + if (locked) + up_read(&mm->mmap_sem); + break; + } + + page_sz = vma_kernel_pagesize(vma); + start = round_down(addr, page_sz); + voffset = addr - start; + addr = start; + vaddr = kmap(page); + this_len = _adi_tag_rw(vaddr, voffset, count, buf, page_sz, + write); + + kunmap(page); + put_page(page); + + if (locked) + up_read(&mm->mmap_sem); + + if (this_len < 0) { + copied = this_len; + break; + } + + addr += page_sz; + copied += this_len; + count -= this_len; + buf += this_len; + } + + if (write) + _membar_sync(); + + return copied; +} + +static ssize_t proc_tag_rw(struct file *file, char __user *buf, size_t count, + loff_t *ppos, int write) +{ + struct task_struct *task = get_proc_task(file_inode(file)); + struct mm_struct *mm = file->private_data; + unsigned long long addr; + ssize_t length; + + if (!adi_capable()) + return -EPERM; + + if (!mm) + return 0; + + if (!atomic_inc_not_zero(&mm->mm_users)) + return 0; + + addr = adi_blksize() * (unsigned long long)*ppos; + length = adi_tag_rw(mm, task, buf, count, addr, write); + + mmput(mm); + + if (length > 0) + *ppos += length; + + return length; +} + +static ssize_t proc_tag_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + return proc_tag_rw(file, buf, count, ppos, 0); +} + +static ssize_t proc_tag_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return proc_tag_rw(file, (char __user *)buf, count, ppos, 1); +} + +static int proc_tag_open(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = proc_mem_open(inode, PTRACE_MODE_ATTACH); + + if (IS_ERR(mm)) + return PTR_ERR(mm); + + file->private_data = mm; + file->f_mode |= FMODE_UNSIGNED_OFFSET; + return 0; +} + +static int proc_tag_release(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = file->private_data; + + if (mm) + mmdrop(mm); + return 0; +} + +static const struct file_operations proc_tag_operations = { + .llseek = mem_lseek, + .open = proc_tag_open, + .read = proc_tag_read, + .release = proc_tag_release, + .write = proc_tag_write +}; + +static const struct pid_entry adi_dir_stuff[] = { + REG("tags", S_IRUSR | S_IWUSR, proc_tag_operations), +}; + +static int proc_adi_readdir(struct file *file, struct dir_context *ctx) +{ + return proc_pident_readdir(file, ctx, adi_dir_stuff, + ARRAY_SIZE(adi_dir_stuff)); +} + +const struct file_operations proc_adi_operations = { + .read = generic_read_dir, + .iterate = proc_adi_readdir, + .llseek = default_llseek, +}; + +static struct dentry *proc_adi_dir_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + return proc_pident_lookup(dir, dentry, adi_dir_stuff, + ARRAY_SIZE(adi_dir_stuff)); +} + +const struct inode_operations proc_adi_inode_operations = { + .lookup = proc_adi_dir_lookup, + .getattr = pid_getattr, + .setattr = proc_setattr, +}; diff --git a/fs/proc/adi.h b/fs/proc/adi.h new file mode 100644 index 000000000000..0e7400ac2a15 --- /dev/null +++ b/fs/proc/adi.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Softare Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef __PROCFS_ADI_H__ +#define __PROCFS_ADI_H__ + +#include + +extern const struct file_operations proc_adi_operations; +extern const struct inode_operations proc_adi_inode_operations; + +#endif /* __PROCFS_ADI_H__ */ diff --git a/fs/proc/base.c b/fs/proc/base.c index 7bb83f30eac5..f8d891e9c065 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -93,6 +93,9 @@ #include #include "internal.h" #include "fd.h" +#ifdef CONFIG_SPARC64 +#include "adi.h" +#endif /* NOTE: * Implementing inode permission operations in /proc is almost @@ -2814,6 +2817,10 @@ static const struct pid_entry tgid_base_stuff[] = { DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), #ifdef CONFIG_NET DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), +#endif +#ifdef CONFIG_SPARC64 + DIR("adi", S_IRUGO | S_IXUGO, proc_adi_inode_operations, + proc_adi_operations), #endif REG("environ", S_IRUSR, proc_environ_operations), ONE("auxv", S_IRUSR, proc_pid_auxv), -- 2.50.1