]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
proc: sparc64 ADI version tag debugging interface
authorEric Snowberg <eric.snowberg@oracle.com>
Fri, 12 May 2017 20:31:42 +0000 (13:31 -0700)
committerAllen Pais <allen.pais@oracle.com>
Tue, 18 Jul 2017 12:25:00 +0000 (17:55 +0530)
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/<pid>/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(â\80\9c/proc/pid/adi/tagsâ\80\9d, 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 <eric.snowberg@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
Reviewed-by: Anthony Yznaga <anthony.yznaga@oracle.com>
arch/sparc/include/asm/adi_64.h
fs/proc/Makefile
fs/proc/adi.c [new file with mode: 0644]
fs/proc/adi.h [new file with mode: 0644]
fs/proc/base.c

index 7fbe92a04495956276e49a80e06de5ab41358a31..fda1c5d5c1e8e3e82bcc124e55626f6a92cf730b 100644 (file)
@@ -8,6 +8,7 @@
 #define __ASM_SPARC64_ADI_H
 
 #include <linux/types.h>
+#include <asm/processor.h>
 
 #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();
index 7151ea428041d7c691f4983a7aeb62857790777a..0f23f05201887a51271f2141dbce37e52992e96e 100644 (file)
@@ -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 (file)
index 0000000..023edc2
--- /dev/null
@@ -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 <asm/asi.h>
+#include <asm/adi_64.h>
+#include <asm/page.h>
+#include <linux/uaccess.h>
+#include <linux/capability.h>
+#include <linux/highmem.h>
+#include <linux/hugetlb.h>
+#include <linux/ptrace.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+#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 (file)
index 0000000..0e7400a
--- /dev/null
@@ -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 <linux/fs.h>
+
+extern const struct file_operations proc_adi_operations;
+extern const struct inode_operations proc_adi_inode_operations;
+
+#endif /* __PROCFS_ADI_H__ */
index 7bb83f30eac599f020560e0701711a0821a334af..f8d891e9c06516e02078046ae243879ea0b37d06 100644 (file)
@@ -93,6 +93,9 @@
 #include <trace/events/oom.h>
 #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),