rpms/kernel/FC-6 linux-2.6-libata-restore-combined-mode.patch, NONE, 1.1 linux-2.6-utrace-core.patch, 1.2, 1.3 linux-2.6-utrace-ptrace-compat-avr32.patch, NONE, 1.1 linux-2.6-utrace-ptrace-compat-ia64.patch, NONE, 1.1 linux-2.6-utrace-ptrace-compat-s390.patch, NONE, 1.1 linux-2.6-utrace-ptrace-compat-sparc64.patch, 1.2, 1.3 linux-2.6-utrace-ptrace-compat.patch, 1.3, 1.4 linux-2.6-utrace-regset-avr32.patch, NONE, 1.1 linux-2.6-utrace-regset-ia64.patch, NONE, 1.1 linux-2.6-utrace-regset-s390.patch, NONE, 1.1 linux-2.6-utrace-regset-sparc64.patch, 1.2, 1.3 linux-2.6-utrace-regset.patch, 1.2, 1.3 linux-2.6-utrace-tracehook-avr32.patch, NONE, 1.1 linux-2.6-utrace-tracehook-ia64.patch, NONE, 1.1 linux-2.6-utrace-tracehook-s390.patch, NONE, 1.1 linux-2.6-utrace-tracehook-sparc64.patch, 1.2, 1.3 linux-2.6-utrace-tracehook-um.patch, 1.2, 1.3 linux-2.6-utrace-tracehook.patch, 1.2, 1.3 kernel-2.6.spec, 1.2991, 1.2992
fedora-cvs-commits at redhat.com
fedora-cvs-commits at redhat.com
Thu Jul 19 17:46:38 UTC 2007
Author: cebbert
Update of /cvs/dist/rpms/kernel/FC-6
In directory cvs.devel.redhat.com:/tmp/cvs-serv6823
Modified Files:
kernel-2.6.spec
Added Files:
linux-2.6-libata-restore-combined-mode.patch
linux-2.6-utrace-core.patch
linux-2.6-utrace-ptrace-compat-avr32.patch
linux-2.6-utrace-ptrace-compat-ia64.patch
linux-2.6-utrace-ptrace-compat-s390.patch
linux-2.6-utrace-ptrace-compat-sparc64.patch
linux-2.6-utrace-ptrace-compat.patch
linux-2.6-utrace-regset-avr32.patch
linux-2.6-utrace-regset-ia64.patch
linux-2.6-utrace-regset-s390.patch
linux-2.6-utrace-regset-sparc64.patch
linux-2.6-utrace-regset.patch
linux-2.6-utrace-tracehook-avr32.patch
linux-2.6-utrace-tracehook-ia64.patch
linux-2.6-utrace-tracehook-s390.patch
linux-2.6-utrace-tracehook-sparc64.patch
linux-2.6-utrace-tracehook-um.patch
linux-2.6-utrace-tracehook.patch
Log Message:
* Thu Jul 19 2007 Chuck Ebbert <cebbert at redhat.com>
- restore the libata COMBINED_MODE hacks
- set CONFIG_NF_CONNTRACK_PROC_COMPAT
- restore the old firewire stack
- new utrace backport
linux-2.6-libata-restore-combined-mode.patch:
drivers/ata/Kconfig | 5 ++
drivers/ata/libata-sff.c | 20 +++++---
drivers/pci/quirks.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/ioport.h | 1
kernel/resource.c | 21 ++++++++
5 files changed, 154 insertions(+), 6 deletions(-)
--- NEW FILE linux-2.6-libata-restore-combined-mode.patch ---
Restore the combined_mode hacks for libata/ide.
---
drivers/ata/Kconfig | 5 ++
drivers/ata/libata-sff.c | 20 +++++---
drivers/pci/quirks.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/ioport.h | 1
kernel/resource.c | 21 ++++++++
5 files changed, 154 insertions(+), 6 deletions(-)
--- linux-2.6.22.noarch.orig/drivers/ata/Kconfig
+++ linux-2.6.22.noarch/drivers/ata/Kconfig
@@ -27,6 +27,11 @@ config ATA_NONSTANDARD
bool
default n
+config SATA_INTEL_COMBINED
+ bool
+ depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX)
+ default y
+
config ATA_ACPI
bool
depends on ACPI && PCI
--- linux-2.6.22.noarch.orig/drivers/ata/libata-sff.c
+++ linux-2.6.22.noarch/drivers/ata/libata-sff.c
@@ -791,13 +791,21 @@ static int ata_init_legacy_port(struct a
}
/* request cmd_port */
- if (request_region(cmd_port, 8, "libata"))
- legacy_dr->cmd_port[port_no] = cmd_port;
- else {
- dev_printk(KERN_WARNING, host->dev,
- "0x%0lX IDE port busy\n", cmd_port);
- return -EBUSY;
+ if (!request_region(cmd_port, 8, "libata")) {
+ struct resource *conflict, res;
+ res.start = cmd_port
+ res.end = res.start + 8 - 1;
+ conflict = ____request_resource(&ioport_resource, &res);
+ while (conflict->child)
+ conflict = ____request_resource(conflict, &res);
+ if (strcmp(conflict->name, "libata"))
+ dev_printk(KERN_WARNING, host->dev,
+ "0x%0lX IDE port busy -- conflict with %s\n",
+ cmd_port, conflict->name);
+ return -EBUSY;
+ }
}
+ legacy_dr->cmd_port[port_no] = cmd_port;
/* iomap cmd and ctl ports */
legacy_dr->cmd_addr[port_no] = ioport_map(cmd_port, 8);
--- linux-2.6.22.noarch.orig/drivers/pci/quirks.c
+++ linux-2.6.22.noarch/drivers/pci/quirks.c
@@ -1304,6 +1304,119 @@ static void __init quirk_alder_ioapic(st
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_alder_ioapic );
#endif
+enum ide_combined_type { COMBINED = 0, IDE = 1, LIBATA = 2 };
+/* Defaults to combined */
+static enum ide_combined_type combined_mode;
+
+static int __init combined_setup(char *str)
+{
+ if (!strncmp(str, "ide", 3))
+ combined_mode = IDE;
+ else if (!strncmp(str, "libata", 6))
+ combined_mode = LIBATA;
+ else /* "combined" or anything else defaults to old behavior */
+ combined_mode = COMBINED;
+
+ return 1;
+}
+__setup("combined_mode=", combined_setup);
+
+#ifdef CONFIG_SATA_INTEL_COMBINED
+static void __devinit quirk_intel_ide_combined(struct pci_dev *pdev)
+{
+ u8 prog, comb, tmp;
+ int ich = 0;
+
+ /*
+ * Narrow down to Intel SATA PCI devices.
+ */
+ switch (pdev->device) {
+ /* PCI ids taken from drivers/scsi/ata_piix.c */
+ case 0x24d1:
+ case 0x24df:
+ case 0x25a3:
+ case 0x25b0:
+ ich = 5;
+ break;
+ case 0x2651:
+ case 0x2652:
+ case 0x2653:
+ case 0x2680: /* ESB2 */
+ ich = 6;
+ break;
+ case 0x27c0:
+ case 0x27c4:
+ ich = 7;
+ break;
+ case 0x2828: /* ICH8M */
+ ich = 8;
+ break;
+ default:
+ /* we do not handle this PCI device */
+ return;
+ }
+
+ /*
+ * Read combined mode register.
+ */
+ pci_read_config_byte(pdev, 0x90, &tmp); /* combined mode reg */
+
+ if (ich == 5) {
+ tmp &= 0x6; /* interesting bits 2:1, PATA primary/secondary */
+ if (tmp == 0x4) /* bits 10x */
+ comb = (1 << 0); /* SATA port 0, PATA port 1 */
+ else if (tmp == 0x6) /* bits 11x */
+ comb = (1 << 2); /* PATA port 0, SATA port 1 */
+ else
+ return; /* not in combined mode */
+ } else {
+ WARN_ON((ich != 6) && (ich != 7) && (ich != 8));
+ tmp &= 0x3; /* interesting bits 1:0 */
+ if (tmp & (1 << 0))
+ comb = (1 << 2); /* PATA port 0, SATA port 1 */
+ else if (tmp & (1 << 1))
+ comb = (1 << 0); /* SATA port 0, PATA port 1 */
+ else
+ return; /* not in combined mode */
+ }
+
+ /*
+ * Read programming interface register.
+ * (Tells us if it's legacy or native mode)
+ */
+ pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
+
+ /* if SATA port is in native mode, we're ok. */
+ if (prog & comb)
+ return;
+
+ /* Don't reserve any so the IDE driver can get them (but only if
+ * combined_mode=ide).
+ */
+ if (combined_mode == IDE)
+ return;
+
+ /* Grab them both for libata if combined_mode=libata. */
+ if (combined_mode == LIBATA) {
+ request_region(0x1f0, 8, "libata"); /* port 0 */
+ request_region(0x170, 8, "libata"); /* port 1 */
+ return;
+ }
+
+ /* SATA port is in legacy mode. Reserve port so that
+ * IDE driver does not attempt to use it. If request_region
+ * fails, it will be obvious at boot time, so we don't bother
+ * checking return values.
+ */
+ if (comb == (1 << 0))
+ request_region(0x1f0, 8, "libata"); /* port 0 */
+ else
+ request_region(0x170, 8, "libata"); /* port 1 */
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_intel_ide_combined );
+#endif /* CONFIG_SATA_INTEL_COMBINED */
+
+
int pcie_mch_quirk;
EXPORT_SYMBOL(pcie_mch_quirk);
--- linux-2.6.22.noarch.orig/include/linux/ioport.h
+++ linux-2.6.22.noarch/include/linux/ioport.h
@@ -99,6 +99,7 @@ extern struct resource ioport_resource;
extern struct resource iomem_resource;
extern int request_resource(struct resource *root, struct resource *new);
+extern struct resource * ____request_resource(struct resource *root, struct resource *new);
extern int release_resource(struct resource *new);
extern int insert_resource(struct resource *parent, struct resource *new);
extern int allocate_resource(struct resource *root, struct resource *new,
--- linux-2.6.22.noarch.orig/kernel/resource.c
+++ linux-2.6.22.noarch/kernel/resource.c
@@ -213,6 +213,27 @@ int request_resource(struct resource *ro
EXPORT_SYMBOL(request_resource);
/**
+ * ____request_resource - reserve a resource, with resource conflict returned
+ * @root: root resource descriptor
+ * @new: resource descriptor desired by caller
+ *
+ * Returns:
+ * On success, NULL is returned.
+ * On error, a pointer to the conflicting resource is returned.
+ */
+struct resource *____request_resource(struct resource *root, struct resource *new)
+{
+ struct resource *conflict;
+
+ write_lock(&resource_lock);
+ conflict = __request_resource(root, new);
+ write_unlock(&resource_lock);
+ return conflict;
+}
+
+EXPORT_SYMBOL(____request_resource);
+
+/**
* release_resource - release a previously reserved resource
* @old: resource pointer
*/
linux-2.6-utrace-core.patch:
Documentation/DocBook/Makefile | 2
Documentation/DocBook/utrace.tmpl | 23
Documentation/utrace.txt | 579 +++++++++
include/linux/sched.h | 5
include/linux/tracehook.h | 85 +
include/linux/utrace.h | 544 +++++++++
init/Kconfig | 18
kernel/Makefile | 1
kernel/utrace.c | 2263 ++++++++++++++++++++++++++++++++++++++
9 files changed, 3502 insertions(+), 18 deletions(-)
View full diff with command:
/usr/bin/cvs -f diff -kk -u -N -r 1.2 -r 1.3 linux-2.6-utrace-core.patch
Index: linux-2.6-utrace-core.patch
===================================================================
RCS file: linux-2.6-utrace-core.patch
diff -N linux-2.6-utrace-core.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-core.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,3793 @@
+[PATCH 3] utrace core
+
+This adds the utrace facility, a new modular interface in the kernel for
+implementing user thread tracing and debugging. This fits on top of the
+tracehook_* layer, so the new code is well-isolated.
+
+The new interface is in <linux/utrace.h>, and Documentation/utrace.txt
+describes it. It allows for multiple separate tracing engines to work in
+parallel without interfering with each other. Higher-level tracing
+facilities can be implemented as loadable kernel modules using this layer.
+
+The new facility is made optional under CONFIG_UTRACE.
+Normal configurations will always want to enable it.
+It's optional to emphasize the clean separation of the code,
+and in case some stripped-down embedded configurations might want to
+omit it to save space (when ptrace and the like can never be used).
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+
+---
+
+ kernel/Makefile | 1
+ kernel/utrace.c | 2263 +++++++++++++++++++++++++++++++++++++
+ Documentation/utrace.txt | 579 +++++++++
+ Documentation/DocBook/Makefile | 2
+ Documentation/DocBook/utrace.tmpl | 23
+ include/linux/tracehook.h | 85 +
+ include/linux/utrace.h | 544 +++++++++
+ include/linux/sched.h | 5
+ init/Kconfig | 20
+ 9 files changed, 3504 insertions(+), 18 deletions(-)
+ create kernel/utrace.c
+ create Documentation/utrace.txt
+ create Documentation/DocBook/utrace.tmpl
+ create include/linux/utrace.h
+
+--- linux-2.6/kernel/Makefile
++++ linux-2.6/kernel/Makefile
+@@ -51,6 +51,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
+ obj-$(CONFIG_UTS_NS) += utsname.o
+ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
+ obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
++obj-$(CONFIG_UTRACE) += utrace.o
+
+ ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
+ # According to Alan Modra <alan at linuxcare.com.au>, the -fno-omit-frame-pointer is
+--- linux-2.6/kernel/utrace.c
++++ linux-2.6/kernel/utrace.c
+@@ -0,0 +1,2263 @@
++/*
++ * utrace infrastructure interface for debugging user processes
++ *
++ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ */
++
++#include <linux/utrace.h>
++#include <linux/tracehook.h>
++#include <linux/err.h>
++#include <linux/sched.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <asm/tracehook.h>
++
++
++#define UTRACE_DEBUG 1
++#ifdef UTRACE_DEBUG
++#define CHECK_INIT(p) atomic_set(&(p)->check_dead, 1)
++#define CHECK_DEAD(p) BUG_ON(!atomic_dec_and_test(&(p)->check_dead))
++#else
++#define CHECK_INIT(p) do { } while (0)
++#define CHECK_DEAD(p) do { } while (0)
++#endif
++
++/*
++ * Per-thread structure task_struct.utrace points to.
++ *
++ * The task itself never has to worry about this going away after
++ * some event is found set in task_struct.utrace_flags.
++ * Once created, this pointer is changed only when the task is quiescent
++ * (TASK_TRACED or TASK_STOPPED with the siglock held, or dead).
++ *
++ * For other parties, the pointer to this is protected by RCU and
++ * task_lock. Since call_rcu is never used while the thread is alive and
++ * using this struct utrace, we can overlay the RCU data structure used
++ * only for a dead struct with some local state used only for a live utrace
++ * on an active thread.
++ */
++struct utrace
++{
++ union {
++ struct rcu_head dead;
++ struct {
++ struct task_struct *cloning;
++ struct utrace_signal *signal;
++ } live;
++ struct {
++ unsigned long flags;
++ } exit;
++ } u;
++
++ struct list_head engines;
++ spinlock_t lock;
++#ifdef UTRACE_DEBUG
++ atomic_t check_dead;
++#endif
++};
++
++static struct kmem_cache *utrace_cachep;
++static struct kmem_cache *utrace_engine_cachep;
++
++static int __init
++utrace_init(void)
++{
++ utrace_cachep =
++ kmem_cache_create("utrace_cache",
++ sizeof(struct utrace), 0,
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
++ utrace_engine_cachep =
++ kmem_cache_create("utrace_engine_cache",
++ sizeof(struct utrace_attached_engine), 0,
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
++ return 0;
++}
++subsys_initcall(utrace_init);
++
++
++/*
++ * Make sure target->utrace is allocated, and return with it locked on
++ * success. This function mediates startup races. The creating parent
++ * task has priority, and other callers will delay here to let its call
++ * succeed and take the new utrace lock first.
++ */
++static struct utrace *
++utrace_first_engine(struct task_struct *target,
++ struct utrace_attached_engine *engine)
++ __acquires(utrace->lock)
++{
++ struct utrace *utrace;
++
++ /*
++ * If this is a newborn thread and we are not the creator,
++ * we have to wait for it. The creator gets the first chance
++ * to attach. The PF_STARTING flag is cleared after its
++ * report_clone hook has had a chance to run.
++ */
++ if (target->flags & PF_STARTING) {
++ utrace = current->utrace;
++ if (utrace == NULL || utrace->u.live.cloning != target) {
++ yield();
++ return (signal_pending(current)
++ ? ERR_PTR(-ERESTARTNOINTR) : NULL);
++ }
++ }
++
++ utrace = kmem_cache_alloc(utrace_cachep, GFP_KERNEL);
++ if (unlikely(utrace == NULL))
++ return ERR_PTR(-ENOMEM);
++
++ utrace->u.live.cloning = NULL;
++ utrace->u.live.signal = NULL;
++ INIT_LIST_HEAD(&utrace->engines);
++ list_add(&engine->entry, &utrace->engines);
++ spin_lock_init(&utrace->lock);
++ CHECK_INIT(utrace);
++
++ spin_lock(&utrace->lock);
++ task_lock(target);
++ if (likely(target->utrace == NULL)) {
++ rcu_assign_pointer(target->utrace, utrace);
++
++ /*
++ * The task_lock protects us against another thread doing
++ * the same thing. We might still be racing against
++ * tracehook_release_task. It's called with ->exit_state
++ * set to EXIT_DEAD and then checks ->utrace with an
++ * smp_mb() in between. If EXIT_DEAD is set, then
++ * release_task might have checked ->utrace already and saw
++ * it NULL; we can't attach. If we see EXIT_DEAD not yet
++ * set after our barrier, then we know release_task will
++ * see our target->utrace pointer.
++ */
++ smp_mb();
++ if (likely(target->exit_state != EXIT_DEAD)) {
++ task_unlock(target);
++ return utrace;
++ }
[...3400 lines suppressed...]
++ * /proc/PID/status, where it is called with only rcu_read_lock held.
++ *
++ * If this engine returns %NULL, another engine may supply the result.
++ */
++ struct task_struct *(*tracer_task)(struct utrace_attached_engine *,
++ struct task_struct *target);
++};
++
++
++/*
++ * These are the exported entry points for tracing engines to use.
++ */
++struct utrace_attached_engine *utrace_attach(struct task_struct *target,
++ int flags,
++ const struct utrace_engine_ops *,
++ void *data);
++int utrace_detach(struct task_struct *target,
++ struct utrace_attached_engine *engine);
++int utrace_set_flags(struct task_struct *target,
++ struct utrace_attached_engine *engine,
++ unsigned long flags);
++int utrace_inject_signal(struct task_struct *target,
++ struct utrace_attached_engine *engine,
++ u32 action, siginfo_t *info,
++ const struct k_sigaction *ka);
++const struct utrace_regset *utrace_regset(struct task_struct *target,
++ struct utrace_attached_engine *,
++ const struct utrace_regset_view *,
++ int which);
++
++
++/*
++ * Hooks in <linux/tracehook.h> call these entry points to the utrace dispatch.
++ */
++int utrace_quiescent(struct task_struct *, struct utrace_signal *);
++void utrace_release_task(struct task_struct *);
++int utrace_get_signal(struct task_struct *, struct pt_regs *,
++ siginfo_t *, struct k_sigaction *);
++void utrace_report_clone(unsigned long clone_flags, struct task_struct *child);
++void utrace_report_vfork_done(pid_t child_pid);
++void utrace_report_exit(long *exit_code);
++void utrace_report_death(struct task_struct *, struct utrace *);
++void utrace_report_delayed_group_leader(struct task_struct *);
++int utrace_report_jctl(int type);
++void utrace_report_exec(struct linux_binprm *bprm, struct pt_regs *regs);
++void utrace_report_syscall(struct pt_regs *regs, int is_exit);
++struct task_struct *utrace_tracer_task(struct task_struct *);
++int utrace_allow_access_process_vm(struct task_struct *);
++int utrace_unsafe_exec(struct task_struct *);
++void utrace_signal_handler_singlestep(struct task_struct *, struct pt_regs *);
++
++/*
++ * <linux/tracehook.h> uses these accessors to avoid #ifdef CONFIG_UTRACE.
++ */
++static inline unsigned long tsk_utrace_flags(struct task_struct *tsk)
++{
++ return tsk->utrace_flags;
++}
++static inline struct utrace *tsk_utrace_struct(struct task_struct *tsk)
++{
++ return tsk->utrace;
++}
++static inline void utrace_init_task(struct task_struct *child)
++{
++ child->utrace_flags = 0;
++ child->utrace = NULL;
++}
++
++#else /* !CONFIG_UTRACE */
++
++static unsigned long tsk_utrace_flags(struct task_struct *tsk)
++{
++ return 0;
++}
++static struct utrace *tsk_utrace_struct(struct task_struct *tsk)
++{
++ return NULL;
++}
++static inline void utrace_init_task(struct task_struct *child)
++{
++}
++
++/*
++ * The calls to these should all be in if (0) and optimized out entirely.
++ * We have stubs here only so tracehook.h doesn't need to #ifdef them
++ * to avoid external references in case of unoptimized compilation.
++ */
++static inline int utrace_quiescent(struct task_struct *tsk, void *ignored)
++{
++ BUG();
++ return 0;
++}
++static inline void utrace_release_task(struct task_struct *tsk)
++{
++ BUG();
++}
++static inline int utrace_get_signal(struct task_struct *tsk,
++ struct pt_regs *regs,
++ siginfo_t *info, struct k_sigaction *ka)
++{
++ BUG();
++ return 0;
++}
++static inline void utrace_report_clone(unsigned long clone_flags,
++ struct task_struct *child)
++{
++ BUG();
++}
++static inline void utrace_report_vfork_done(pid_t child_pid)
++{
++ BUG();
++}
++static inline void utrace_report_exit(long *exit_code)
++{
++ BUG();
++}
++static inline void utrace_report_death(struct task_struct *tsk, void *ignored)
++{
++ BUG();
++}
++static inline void utrace_report_delayed_group_leader(struct task_struct *tsk)
++{
++ BUG();
++}
++static inline int utrace_report_jctl(int type)
++{
++ BUG();
++ return 0;
++}
++static inline void utrace_report_exec(struct linux_binprm *bprm,
++ struct pt_regs *regs)
++{
++ BUG();
++}
++static inline void utrace_report_syscall(struct pt_regs *regs, int is_exit)
++{
++ BUG();
++}
++static inline struct task_struct *utrace_tracer_task(struct task_struct *tsk)
++{
++ BUG();
++ return NULL;
++}
++static inline int utrace_allow_access_process_vm(struct task_struct *tsk)
++{
++ BUG();
++ return 0;
++}
++static inline int utrace_unsafe_exec(struct task_struct *tsk)
++{
++ BUG();
++ return 0;
++}
++static inline void utrace_signal_handler_singlestep(struct task_struct *tsk,
++ struct pt_regs *regs)
++{
++ BUG();
++}
++
++#endif /* CONFIG_UTRACE */
++
++#endif /* linux/utrace.h */
+--- linux-2.6/include/linux/sched.h
++++ linux-2.6/include/linux/sched.h
+@@ -1029,6 +1029,11 @@ struct task_struct {
+ struct audit_context *audit_context;
+ seccomp_t seccomp;
+
++#ifdef CONFIG_UTRACE
++ struct utrace *utrace;
++ unsigned long utrace_flags;
++#endif
++
+ /* Thread group tracking */
+ u32 parent_exec_id;
+ u32 self_exec_id;
+--- linux-2.6/init/Kconfig
++++ linux-2.6/init/Kconfig
+@@ -686,3 +686,21 @@ config STOP_MACHINE
+ menu "Block layer"
+ source "block/Kconfig"
+ endmenu
++
++menu "Process debugging support"
++
++config UTRACE
++ bool "Infrastructure for tracing and debugging user processes"
++ default y
++ depends on MODULES
++ help
++ Enable the utrace process tracing interface.
++ This is an internal kernel interface to track events in user
++ threads, extract and change user thread state. This interface
++ is exported to kernel modules, and is also used to implement ptrace.
++ If you disable this, no facilities for debugging user processes
++ will be available, nor the facilities used by UML and other
++ applications. Unless you are making a specially stripped-down
++ kernel and are very sure you don't need these facilitiies,
++ say Y.
++endmenu
linux-2.6-utrace-ptrace-compat-avr32.patch:
ptrace.c | 107 ++++++++++-----------------------------------------------------
1 files changed, 18 insertions(+), 89 deletions(-)
--- NEW FILE linux-2.6-utrace-ptrace-compat-avr32.patch ---
[PATCH 4d] utrace: avr32 ptrace compatibility
From: Haavard Skinnemoen <hskinnemoen at atmel.com>
Rip out most of the ptrace code for AVR32 and replace it with the much
nicer utrace stuff. It builds in all possible combinations of
CONFIG_UTRACE and CONFIG_PTRACE, and it seems to work as far as I've tested
it with strace and some simple debugging with gdb.
Signed-off-by: Haavard Skinnemoen <hskinnemoen at atmel.com>
Signed-off-by: Roland McGrath <roland at redhat.com>
---
arch/avr32/kernel/ptrace.c | 107 +++++++-------------------------------------
1 files changed, 18 insertions(+), 89 deletions(-)
--- linux-2.6/arch/avr32/kernel/ptrace.c
+++ linux-2.6/arch/avr32/kernel/ptrace.c
@@ -78,115 +78,44 @@ const struct utrace_regset_view *utrace_
}
#endif /* CONFIG_UTRACE */
+#ifdef CONFIG_PTRACE
-#if 0
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+static const struct ptrace_layout_segment avr32_uarea[] = {
+ { 0, ELF_NGREG * sizeof(long), 0, 0 },
+ { 0, 0, -1, 0 },
+};
+
+int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
{
- unsigned long tmp;
- int ret;
-
pr_debug("arch_ptrace(%ld, %d, %#lx, %#lx)\n",
- request, child->pid, addr, data);
+ *request, child->pid, addr, data);
pr_debug("ptrace: Enabling monitor mode...\n");
__mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE);
- switch (request) {
- /* Read the word at location addr in the child process */
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- if (ret == sizeof(tmp))
- ret = put_user(tmp, (unsigned long __user *)data);
- else
- ret = -EIO;
- break;
-
+ switch (*request) {
case PTRACE_PEEKUSR:
- ret = ptrace_read_user(child, addr,
- (unsigned long __user *)data);
- break;
-
- /* Write the word in data at location addr */
- case PTRACE_POKETEXT:
- case PTRACE_POKEDATA:
- ret = access_process_vm(child, addr, &data, sizeof(data), 1);
- if (ret == sizeof(data))
- ret = 0;
- else
- ret = -EIO;
- break;
+ return ptrace_peekusr(child, engine, avr32_uarea, addr, data);
case PTRACE_POKEUSR:
- ret = ptrace_write_user(child, addr, data);
- break;
-
- /* continue and stop at next (return from) syscall */
- case PTRACE_SYSCALL:
- /* restart after signal */
- case PTRACE_CONT:
- ret = -EIO;
- if (!valid_signal(data))
- break;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
- /* XXX: Are we sure no breakpoints are active here? */
- wake_up_process(child);
- ret = 0;
- break;
-
- /*
- * Make the child exit. Best I can do is send it a
- * SIGKILL. Perhaps it should be put in the status that it
- * wants to exit.
- */
- case PTRACE_KILL:
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE)
- break;
- child->exit_code = SIGKILL;
- wake_up_process(child);
- break;
-
- /*
- * execute single instruction.
- */
- case PTRACE_SINGLESTEP:
- ret = -EIO;
- if (!valid_signal(data))
- break;
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- ptrace_single_step(child);
- child->exit_code = data;
- wake_up_process(child);
- ret = 0;
- break;
-
- /* Detach a process that was attached */
- case PTRACE_DETACH:
- ret = ptrace_detach(child, data);
+ return ptrace_pokeusr(child, engine, avr32_uarea, addr, data);
break;
case PTRACE_GETREGS:
- ret = ptrace_getregs(child, (void __user *)data);
+ return ptrace_whole_regset(child, engine, data, 0, 0);
break;
case PTRACE_SETREGS:
- ret = ptrace_setregs(child, (const void __user *)data);
- break;
-
- default:
- ret = ptrace_request(child, request, addr, data);
+ return ptrace_whole_regset(child, engine, data, 0, 1);
break;
}
- pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC));
- return ret;
+ return -ENOSYS;
}
-#endif
+#endif /* CONFIG_PTRACE */
+#endif /* CONFIG_UTRACE */
asmlinkage void syscall_trace(struct pt_regs *regs, int is_exit)
{
linux-2.6-utrace-ptrace-compat-ia64.patch:
ia32/sys_ia32.c | 40 ++
kernel/ptrace.c | 1016 ++++++--------------------------------------------------
2 files changed, 159 insertions(+), 897 deletions(-)
--- NEW FILE linux-2.6-utrace-ptrace-compat-ia64.patch ---
[PATCH 4a] utrace: ia64 ptrace compatibility
This patch implements ptrace compatibility for ia64.
Signed-off-by: Roland McGrath <roland at redhat.com>
Signed-off-by: Anil S Keshavamurthy <anil.s.keshavamurthy at intel.com>
Signed-off-by: Bibo mao <bibo.mao at intel.com>
---
arch/ia64/ia32/sys_ia32.c | 40 ++
arch/ia64/kernel/ptrace.c | 1016 +++++----------------------------------------
2 files changed, 159 insertions(+), 897 deletions(-)
--- linux-2.6/arch/ia64/ia32/sys_ia32.c
+++ linux-2.6/arch/ia64/ia32/sys_ia32.c
@@ -2340,6 +2340,46 @@ const struct utrace_regset_view utrace_i
};
#endif
+#ifdef CONFIG_PTRACE
+/*
+ * This matches the arch/i386/kernel/ptrace.c definitions.
+ */
+
+static const struct ptrace_layout_segment ia32_uarea[] = {
+ {0, sizeof(struct user_regs_struct32), 0, 0},
+ {0, 0, -1, 0}
+};
+
+fastcall int arch_compat_ptrace(compat_long_t *request,
+ struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t addr, compat_ulong_t data,
+ compat_long_t *retval)
+{
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+ return ptrace_compat_peekusr(child, engine, ia32_uarea,
+ addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_compat_pokeusr(child, engine, ia32_uarea,
+ addr, data);
+ case IA32_PTRACE_GETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 0);
+ case IA32_PTRACE_SETREGS:
+ return ptrace_whole_regset(child, engine, data, 0, 1);
+ case IA32_PTRACE_GETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 0);
+ case IA32_PTRACE_SETFPREGS:
+ return ptrace_whole_regset(child, engine, data, 1, 1);
+ case IA32_PTRACE_GETFPXREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 0);
+ case IA32_PTRACE_SETFPXREGS:
+ return ptrace_whole_regset(child, engine, data, 2, 1);
+ }
+ return -ENOSYS;
+}
+#endif
+
typedef struct {
unsigned int ss_sp;
unsigned int ss_flags;
--- linux-2.6/arch/ia64/kernel/ptrace.c
+++ linux-2.6/arch/ia64/kernel/ptrace.c
@@ -554,81 +554,6 @@ ia64_sync_user_rbs (struct task_struct *
return 0;
}
-#if 0 /* XXX */
-static inline int
-thread_matches (struct task_struct *thread, unsigned long addr)
-{
- unsigned long thread_rbs_end;
- struct pt_regs *thread_regs;
-
- if (ptrace_check_attach(thread, 0) < 0)
- /*
- * If the thread is not in an attachable state, we'll
- * ignore it. The net effect is that if ADDR happens
- * to overlap with the portion of the thread's
- * register backing store that is currently residing
- * on the thread's kernel stack, then ptrace() may end
- * up accessing a stale value. But if the thread
- * isn't stopped, that's a problem anyhow, so we're
- * doing as well as we can...
- */
- return 0;
-
- thread_regs = task_pt_regs(thread);
- thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL);
- if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end))
- return 0;
-
- return 1; /* looks like we've got a winner */
-}
-
-/*
- * GDB apparently wants to be able to read the register-backing store
- * of any thread when attached to a given process. If we are peeking
- * or poking an address that happens to reside in the kernel-backing
- * store of another thread, we need to attach to that thread, because
- * otherwise we end up accessing stale data.
- *
- * task_list_lock must be read-locked before calling this routine!
- */
-static struct task_struct *
-find_thread_for_addr (struct task_struct *child, unsigned long addr)
-{
- struct task_struct *p;
- struct mm_struct *mm;
- struct list_head *this, *next;
- int mm_users;
-
- if (!(mm = get_task_mm(child)))
- return child;
-
- /* -1 because of our get_task_mm(): */
- mm_users = atomic_read(&mm->mm_users) - 1;
- if (mm_users <= 1)
- goto out; /* not multi-threaded */
-
- /*
- * Traverse the current process' children list. Every task that
- * one attaches to becomes a child. And it is only attached children
- * of the debugger that are of interest (ptrace_check_attach checks
- * for this).
- */
- list_for_each_safe(this, next, ¤t->children) {
- p = list_entry(this, struct task_struct, sibling);
- if (p->tgid != child->tgid)
- continue;
- if (thread_matches(p, addr)) {
- child = p;
- goto out;
- }
- }
-
- out:
- mmput(mm);
- return child;
-}
-#endif
-
/*
* Write f32-f127 back to task->thread.fph if it has been modified.
*/
@@ -792,828 +717,6 @@ access_nat_bits (struct task_struct *chi
return 0;
}
-#if 0
-static int
-access_uarea (struct task_struct *child, unsigned long addr,
- unsigned long *data, int write_access)
-{
- unsigned long *ptr, regnum, urbs_end, rnat_addr, cfm;
- struct switch_stack *sw;
- struct pt_regs *pt;
-# define pt_reg_addr(pt, reg) ((void *) \
- ((unsigned long) (pt) \
- + offsetof(struct pt_regs, reg)))
-
-
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
-
- if ((addr & 0x7) != 0) {
- dprintk("ptrace: unaligned register address 0x%lx\n", addr);
- return -1;
- }
-
- if (addr < PT_F127 + 16) {
- /* accessing fph */
- if (write_access)
- ia64_sync_fph(child);
- else
- ia64_flush_fph(child);
- ptr = (unsigned long *)
- ((unsigned long) &child->thread.fph + addr);
- } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) {
- /* scratch registers untouched by kernel (saved in pt_regs) */
- ptr = pt_reg_addr(pt, f10) + (addr - PT_F10);
- } else if (addr >= PT_F12 && addr < PT_F15 + 16) {
- /*
- * Scratch registers untouched by kernel (saved in
- * switch_stack).
- */
- ptr = (unsigned long *) ((long) sw
- + (addr - PT_NAT_BITS - 32));
- } else if (addr < PT_AR_LC + 8) {
- /* preserved state: */
- struct unw_frame_info info;
- char nat = 0;
- int ret;
-
- unw_init_from_blocked_task(&info, child);
- if (unw_unwind_to_user(&info) < 0)
- return -1;
-
- switch (addr) {
- case PT_NAT_BITS:
- return access_nat_bits(child, pt, &info,
- data, write_access);
-
- case PT_R4: case PT_R5: case PT_R6: case PT_R7:
- if (write_access) {
- /* read NaT bit first: */
- unsigned long dummy;
-
- ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4,
- &dummy, &nat);
- if (ret < 0)
- return ret;
- }
- return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data,
- &nat, write_access);
-
- case PT_B1: case PT_B2: case PT_B3:
- case PT_B4: case PT_B5:
- return unw_access_br(&info, (addr - PT_B1)/8 + 1, data,
- write_access);
-
- case PT_AR_EC:
- return unw_access_ar(&info, UNW_AR_EC, data,
- write_access);
-
- case PT_AR_LC:
- return unw_access_ar(&info, UNW_AR_LC, data,
- write_access);
-
- default:
- if (addr >= PT_F2 && addr < PT_F5 + 16)
- return access_fr(&info, (addr - PT_F2)/16 + 2,
- (addr & 8) != 0, data,
- write_access);
- else if (addr >= PT_F16 && addr < PT_F31 + 16)
- return access_fr(&info,
- (addr - PT_F16)/16 + 16,
- (addr & 8) != 0,
- data, write_access);
- else {
- dprintk("ptrace: rejecting access to register "
- "address 0x%lx\n", addr);
- return -1;
- }
- }
- } else if (addr < PT_F9+16) {
- /* scratch state */
- switch (addr) {
- case PT_AR_BSP:
- /*
- * By convention, we use PT_AR_BSP to refer to
- * the end of the user-level backing store.
- * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof)
- * to get the real value of ar.bsp at the time
- * the kernel was entered.
- *
- * Furthermore, when changing the contents of
- * PT_AR_BSP (or PT_CFM) we MUST copy any
- * users-level stacked registers that are
- * stored on the kernel stack back to
- * user-space because otherwise, we might end
- * up clobbering kernel stacked registers.
- * Also, if this happens while the task is
- * blocked in a system call, which convert the
- * state such that the non-system-call exit
- * path is used. This ensures that the proper
- * state will be picked up when resuming
- * execution. However, it *also* means that
- * once we write PT_AR_BSP/PT_CFM, it won't be
- * possible to modify the syscall arguments of
- * the pending system call any longer. This
- * shouldn't be an issue because modifying
- * PT_AR_BSP/PT_CFM generally implies that
- * we're either abandoning the pending system
- * call or that we defer it's re-execution
- * (e.g., due to GDB doing an inferior
- * function call).
- */
- urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
- if (write_access) {
- if (*data != urbs_end) {
- if (ia64_sync_user_rbs(child, sw,
- pt->ar_bspstore,
- urbs_end) < 0)
- return -1;
- if (in_syscall(pt))
- convert_to_non_syscall(child,
- pt,
- cfm);
- /*
- * Simulate user-level write
- * of ar.bsp:
- */
- pt->loadrs = 0;
- pt->ar_bspstore = *data;
- }
- } else
- *data = urbs_end;
- return 0;
-
- case PT_CFM:
- urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
- if (write_access) {
- if (((cfm ^ *data) & PFM_MASK) != 0) {
- if (ia64_sync_user_rbs(child, sw,
- pt->ar_bspstore,
- urbs_end) < 0)
- return -1;
- if (in_syscall(pt))
- convert_to_non_syscall(child,
- pt,
- cfm);
- pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK)
- | (*data & PFM_MASK));
- }
- } else
- *data = cfm;
- return 0;
-
- case PT_CR_IPSR:
- if (write_access)
- pt->cr_ipsr = ((*data & IPSR_MASK)
- | (pt->cr_ipsr & ~IPSR_MASK));
- else
- *data = (pt->cr_ipsr & IPSR_MASK);
- return 0;
-
- case PT_AR_RSC:
- if (write_access)
- pt->ar_rsc = *data | (3 << 2); /* force PL3 */
- else
- *data = pt->ar_rsc;
- return 0;
-
- case PT_AR_RNAT:
- urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
- rnat_addr = (long) ia64_rse_rnat_addr((long *)
- urbs_end);
- if (write_access)
- return ia64_poke(child, sw, urbs_end,
- rnat_addr, *data);
- else
- return ia64_peek(child, sw, urbs_end,
- rnat_addr, data);
-
- case PT_R1:
- ptr = pt_reg_addr(pt, r1);
- break;
- case PT_R2: case PT_R3:
- ptr = pt_reg_addr(pt, r2) + (addr - PT_R2);
- break;
- case PT_R8: case PT_R9: case PT_R10: case PT_R11:
- ptr = pt_reg_addr(pt, r8) + (addr - PT_R8);
- break;
- case PT_R12: case PT_R13:
- ptr = pt_reg_addr(pt, r12) + (addr - PT_R12);
- break;
- case PT_R14:
- ptr = pt_reg_addr(pt, r14);
- break;
- case PT_R15:
- ptr = pt_reg_addr(pt, r15);
- break;
- case PT_R16: case PT_R17: case PT_R18: case PT_R19:
- case PT_R20: case PT_R21: case PT_R22: case PT_R23:
- case PT_R24: case PT_R25: case PT_R26: case PT_R27:
- case PT_R28: case PT_R29: case PT_R30: case PT_R31:
- ptr = pt_reg_addr(pt, r16) + (addr - PT_R16);
- break;
- case PT_B0:
- ptr = pt_reg_addr(pt, b0);
- break;
- case PT_B6:
- ptr = pt_reg_addr(pt, b6);
- break;
- case PT_B7:
- ptr = pt_reg_addr(pt, b7);
- break;
- case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8:
- case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8:
- ptr = pt_reg_addr(pt, f6) + (addr - PT_F6);
- break;
- case PT_AR_BSPSTORE:
- ptr = pt_reg_addr(pt, ar_bspstore);
- break;
- case PT_AR_UNAT:
- ptr = pt_reg_addr(pt, ar_unat);
- break;
- case PT_AR_PFS:
- ptr = pt_reg_addr(pt, ar_pfs);
- break;
- case PT_AR_CCV:
- ptr = pt_reg_addr(pt, ar_ccv);
- break;
- case PT_AR_FPSR:
- ptr = pt_reg_addr(pt, ar_fpsr);
- break;
- case PT_CR_IIP:
- ptr = pt_reg_addr(pt, cr_iip);
- break;
- case PT_PR:
- ptr = pt_reg_addr(pt, pr);
- break;
- /* scratch register */
-
- default:
- /* disallow accessing anything else... */
- dprintk("ptrace: rejecting access to register "
- "address 0x%lx\n", addr);
- return -1;
- }
- } else if (addr <= PT_AR_SSD) {
- ptr = pt_reg_addr(pt, ar_csd) + (addr - PT_AR_CSD);
- } else {
- /* access debug registers */
-
- if (addr >= PT_IBR) {
- regnum = (addr - PT_IBR) >> 3;
- ptr = &child->thread.ibr[0];
- } else {
- regnum = (addr - PT_DBR) >> 3;
- ptr = &child->thread.dbr[0];
- }
-
- if (regnum >= 8) {
- dprintk("ptrace: rejecting access to register "
- "address 0x%lx\n", addr);
- return -1;
- }
-#ifdef CONFIG_PERFMON
- /*
- * Check if debug registers are used by perfmon. This
- * test must be done once we know that we can do the
- * operation, i.e. the arguments are all valid, but
- * before we start modifying the state.
- *
- * Perfmon needs to keep a count of how many processes
- * are trying to modify the debug registers for system
- * wide monitoring sessions.
- *
- * We also include read access here, because they may
- * cause the PMU-installed debug register state
- * (dbr[], ibr[]) to be reset. The two arrays are also
- * used by perfmon, but we do not use
- * IA64_THREAD_DBG_VALID. The registers are restored
- * by the PMU context switch code.
- */
- if (pfm_use_debug_registers(child)) return -1;
-#endif
-
- if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) {
- child->thread.flags |= IA64_THREAD_DBG_VALID;
- memset(child->thread.dbr, 0,
- sizeof(child->thread.dbr));
- memset(child->thread.ibr, 0,
- sizeof(child->thread.ibr));
- }
-
- ptr += regnum;
-
- if ((regnum & 1) && write_access) {
- /* don't let the user set kernel-level breakpoints: */
- *ptr = *data & ~(7UL << 56);
- return 0;
- }
- }
- if (write_access)
- *ptr = *data;
- else
- *data = *ptr;
- return 0;
-}
-
-static long
-ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
-{
- unsigned long psr, ec, lc, rnat, bsp, cfm, nat_bits, val;
- struct unw_frame_info info;
- struct ia64_fpreg fpval;
- struct switch_stack *sw;
- struct pt_regs *pt;
- long ret, retval = 0;
- char nat = 0;
- int i;
-
- if (!access_ok(VERIFY_WRITE, ppr, sizeof(struct pt_all_user_regs)))
- return -EIO;
-
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
- unw_init_from_blocked_task(&info, child);
- if (unw_unwind_to_user(&info) < 0) {
- return -EIO;
- }
-
- if (((unsigned long) ppr & 0x7) != 0) {
- dprintk("ptrace:unaligned register address %p\n", ppr);
- return -EIO;
- }
-
- if (access_uarea(child, PT_CR_IPSR, &psr, 0) < 0
- || access_uarea(child, PT_AR_EC, &ec, 0) < 0
- || access_uarea(child, PT_AR_LC, &lc, 0) < 0
- || access_uarea(child, PT_AR_RNAT, &rnat, 0) < 0
- || access_uarea(child, PT_AR_BSP, &bsp, 0) < 0
- || access_uarea(child, PT_CFM, &cfm, 0)
- || access_uarea(child, PT_NAT_BITS, &nat_bits, 0))
- return -EIO;
-
- /* control regs */
-
- retval |= __put_user(pt->cr_iip, &ppr->cr_iip);
- retval |= __put_user(psr, &ppr->cr_ipsr);
-
- /* app regs */
-
- retval |= __put_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]);
- retval |= __put_user(pt->ar_rsc, &ppr->ar[PT_AUR_RSC]);
- retval |= __put_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]);
- retval |= __put_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]);
- retval |= __put_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
- retval |= __put_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]);
-
- retval |= __put_user(ec, &ppr->ar[PT_AUR_EC]);
- retval |= __put_user(lc, &ppr->ar[PT_AUR_LC]);
- retval |= __put_user(rnat, &ppr->ar[PT_AUR_RNAT]);
- retval |= __put_user(bsp, &ppr->ar[PT_AUR_BSP]);
- retval |= __put_user(cfm, &ppr->cfm);
-
- /* gr1-gr3 */
-
- retval |= __copy_to_user(&ppr->gr[1], &pt->r1, sizeof(long));
- retval |= __copy_to_user(&ppr->gr[2], &pt->r2, sizeof(long) *2);
-
- /* gr4-gr7 */
-
- for (i = 4; i < 8; i++) {
- if (unw_access_gr(&info, i, &val, &nat, 0) < 0)
- return -EIO;
- retval |= __put_user(val, &ppr->gr[i]);
- }
-
- /* gr8-gr11 */
-
- retval |= __copy_to_user(&ppr->gr[8], &pt->r8, sizeof(long) * 4);
-
- /* gr12-gr15 */
-
- retval |= __copy_to_user(&ppr->gr[12], &pt->r12, sizeof(long) * 2);
- retval |= __copy_to_user(&ppr->gr[14], &pt->r14, sizeof(long));
- retval |= __copy_to_user(&ppr->gr[15], &pt->r15, sizeof(long));
-
- /* gr16-gr31 */
-
- retval |= __copy_to_user(&ppr->gr[16], &pt->r16, sizeof(long) * 16);
-
- /* b0 */
-
- retval |= __put_user(pt->b0, &ppr->br[0]);
-
- /* b1-b5 */
-
- for (i = 1; i < 6; i++) {
- if (unw_access_br(&info, i, &val, 0) < 0)
- return -EIO;
- __put_user(val, &ppr->br[i]);
- }
-
- /* b6-b7 */
-
- retval |= __put_user(pt->b6, &ppr->br[6]);
- retval |= __put_user(pt->b7, &ppr->br[7]);
-
- /* fr2-fr5 */
-
- for (i = 2; i < 6; i++) {
- if (unw_get_fr(&info, i, &fpval) < 0)
- return -EIO;
- retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval));
- }
-
- /* fr6-fr11 */
-
- retval |= __copy_to_user(&ppr->fr[6], &pt->f6,
- sizeof(struct ia64_fpreg) * 6);
-
- /* fp scratch regs(12-15) */
-
- retval |= __copy_to_user(&ppr->fr[12], &sw->f12,
- sizeof(struct ia64_fpreg) * 4);
-
- /* fr16-fr31 */
-
- for (i = 16; i < 32; i++) {
- if (unw_get_fr(&info, i, &fpval) < 0)
- return -EIO;
- retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval));
- }
-
- /* fph */
-
- ia64_flush_fph(child);
- retval |= __copy_to_user(&ppr->fr[32], &child->thread.fph,
- sizeof(ppr->fr[32]) * 96);
-
- /* preds */
-
- retval |= __put_user(pt->pr, &ppr->pr);
-
- /* nat bits */
-
- retval |= __put_user(nat_bits, &ppr->nat);
-
- ret = retval ? -EIO : 0;
- return ret;
-}
-#endif /* ptrace_getregs() */
-
-#if 0
-static long
-ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
-{
- unsigned long psr, rsc, ec, lc, rnat, bsp, cfm, nat_bits, val = 0;
- struct unw_frame_info info;
- struct switch_stack *sw;
- struct ia64_fpreg fpval;
- struct pt_regs *pt;
- long ret, retval = 0;
- int i;
-
- memset(&fpval, 0, sizeof(fpval));
-
- if (!access_ok(VERIFY_READ, ppr, sizeof(struct pt_all_user_regs)))
- return -EIO;
-
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
- unw_init_from_blocked_task(&info, child);
- if (unw_unwind_to_user(&info) < 0) {
- return -EIO;
- }
-
- if (((unsigned long) ppr & 0x7) != 0) {
- dprintk("ptrace:unaligned register address %p\n", ppr);
- return -EIO;
- }
-
- /* control regs */
-
- retval |= __get_user(pt->cr_iip, &ppr->cr_iip);
- retval |= __get_user(psr, &ppr->cr_ipsr);
-
- /* app regs */
-
- retval |= __get_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]);
- retval |= __get_user(rsc, &ppr->ar[PT_AUR_RSC]);
- retval |= __get_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]);
- retval |= __get_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]);
- retval |= __get_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
- retval |= __get_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]);
-
- retval |= __get_user(ec, &ppr->ar[PT_AUR_EC]);
- retval |= __get_user(lc, &ppr->ar[PT_AUR_LC]);
- retval |= __get_user(rnat, &ppr->ar[PT_AUR_RNAT]);
- retval |= __get_user(bsp, &ppr->ar[PT_AUR_BSP]);
- retval |= __get_user(cfm, &ppr->cfm);
-
- /* gr1-gr3 */
-
- retval |= __copy_from_user(&pt->r1, &ppr->gr[1], sizeof(long));
- retval |= __copy_from_user(&pt->r2, &ppr->gr[2], sizeof(long) * 2);
-
- /* gr4-gr7 */
-
- for (i = 4; i < 8; i++) {
- retval |= __get_user(val, &ppr->gr[i]);
- /* NaT bit will be set via PT_NAT_BITS: */
- if (unw_set_gr(&info, i, val, 0) < 0)
- return -EIO;
- }
-
- /* gr8-gr11 */
-
- retval |= __copy_from_user(&pt->r8, &ppr->gr[8], sizeof(long) * 4);
-
- /* gr12-gr15 */
-
- retval |= __copy_from_user(&pt->r12, &ppr->gr[12], sizeof(long) * 2);
- retval |= __copy_from_user(&pt->r14, &ppr->gr[14], sizeof(long));
- retval |= __copy_from_user(&pt->r15, &ppr->gr[15], sizeof(long));
-
- /* gr16-gr31 */
-
- retval |= __copy_from_user(&pt->r16, &ppr->gr[16], sizeof(long) * 16);
-
- /* b0 */
-
- retval |= __get_user(pt->b0, &ppr->br[0]);
-
- /* b1-b5 */
-
- for (i = 1; i < 6; i++) {
- retval |= __get_user(val, &ppr->br[i]);
- unw_set_br(&info, i, val);
- }
-
- /* b6-b7 */
-
- retval |= __get_user(pt->b6, &ppr->br[6]);
- retval |= __get_user(pt->b7, &ppr->br[7]);
-
- /* fr2-fr5 */
-
- for (i = 2; i < 6; i++) {
- retval |= __copy_from_user(&fpval, &ppr->fr[i], sizeof(fpval));
- if (unw_set_fr(&info, i, fpval) < 0)
- return -EIO;
- }
-
- /* fr6-fr11 */
-
- retval |= __copy_from_user(&pt->f6, &ppr->fr[6],
- sizeof(ppr->fr[6]) * 6);
-
- /* fp scratch regs(12-15) */
-
- retval |= __copy_from_user(&sw->f12, &ppr->fr[12],
- sizeof(ppr->fr[12]) * 4);
-
- /* fr16-fr31 */
-
- for (i = 16; i < 32; i++) {
- retval |= __copy_from_user(&fpval, &ppr->fr[i],
- sizeof(fpval));
- if (unw_set_fr(&info, i, fpval) < 0)
- return -EIO;
- }
-
- /* fph */
-
- ia64_sync_fph(child);
- retval |= __copy_from_user(&child->thread.fph, &ppr->fr[32],
- sizeof(ppr->fr[32]) * 96);
-
- /* preds */
-
- retval |= __get_user(pt->pr, &ppr->pr);
-
- /* nat bits */
-
- retval |= __get_user(nat_bits, &ppr->nat);
-
- retval |= access_uarea(child, PT_CR_IPSR, &psr, 1);
- retval |= access_uarea(child, PT_AR_RSC, &rsc, 1);
- retval |= access_uarea(child, PT_AR_EC, &ec, 1);
- retval |= access_uarea(child, PT_AR_LC, &lc, 1);
- retval |= access_uarea(child, PT_AR_RNAT, &rnat, 1);
- retval |= access_uarea(child, PT_AR_BSP, &bsp, 1);
- retval |= access_uarea(child, PT_CFM, &cfm, 1);
- retval |= access_uarea(child, PT_NAT_BITS, &nat_bits, 1);
-
- ret = retval ? -EIO : 0;
- return ret;
-}
-#endif /* ptrace_setregs() */
-
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
- */
-void
-ptrace_disable (struct task_struct *child)
-{
- struct ia64_psr *child_psr = ia64_psr(task_pt_regs(child));
-
- /* make sure the single step/taken-branch trap bits are not set: */
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- child_psr->ss = 0;
- child_psr->tb = 0;
-}
-
-#if 0 /* XXX */
-asmlinkage long
-sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
-{
- struct pt_regs *pt;
- unsigned long urbs_end, peek_or_poke;
- struct task_struct *child;
- struct switch_stack *sw;
- long ret;
-
- lock_kernel();
- ret = -EPERM;
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
- goto out;
- }
-
- peek_or_poke = (request == PTRACE_PEEKTEXT
- || request == PTRACE_PEEKDATA
- || request == PTRACE_POKETEXT
- || request == PTRACE_POKEDATA);
- ret = -ESRCH;
- read_lock(&tasklist_lock);
- {
- child = find_task_by_pid(pid);
- if (child) {
- if (peek_or_poke)
- child = find_thread_for_addr(child, addr);
- get_task_struct(child);
- }
- }
- read_unlock(&tasklist_lock);
- if (!child)
- goto out;
- ret = -EPERM;
- if (pid == 1) /* no messing around with init! */
- goto out_tsk;
-
- if (request == PTRACE_ATTACH) {
- ret = ptrace_attach(child);
- goto out_tsk;
- }
-
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- goto out_tsk;
-
- pt = task_pt_regs(child);
- sw = (struct switch_stack *) (child->thread.ksp + 16);
-
- switch (request) {
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- /* read word at location addr */
- urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
- ret = ia64_peek(child, sw, urbs_end, addr, &data);
- if (ret == 0) {
- ret = data;
- /* ensure "ret" is not mistaken as an error code: */
- force_successful_syscall_return();
- }
- goto out_tsk;
-
- case PTRACE_POKETEXT:
- case PTRACE_POKEDATA:
- /* write the word at location addr */
- urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
- ret = ia64_poke(child, sw, urbs_end, addr, data);
- goto out_tsk;
-
- case PTRACE_PEEKUSR:
- /* read the word at addr in the USER area */
- if (access_uarea(child, addr, &data, 0) < 0) {
- ret = -EIO;
- goto out_tsk;
- }
- ret = data;
- /* ensure "ret" is not mistaken as an error code */
- force_successful_syscall_return();
- goto out_tsk;
-
- case PTRACE_POKEUSR:
- /* write the word at addr in the USER area */
- if (access_uarea(child, addr, &data, 1) < 0) {
- ret = -EIO;
- goto out_tsk;
- }
- ret = 0;
- goto out_tsk;
-
- case PTRACE_OLD_GETSIGINFO:
- /* for backwards-compatibility */
- ret = ptrace_request(child, PTRACE_GETSIGINFO, addr, data);
- goto out_tsk;
-
- case PTRACE_OLD_SETSIGINFO:
- /* for backwards-compatibility */
- ret = ptrace_request(child, PTRACE_SETSIGINFO, addr, data);
- goto out_tsk;
-
- case PTRACE_SYSCALL:
- /* continue and stop at next (return from) syscall */
- case PTRACE_CONT:
- /* restart after signal. */
- ret = -EIO;
- if (!valid_signal(data))
- goto out_tsk;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
-
- /*
- * Make sure the single step/taken-branch trap bits
- * are not set:
- */
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- ia64_psr(pt)->ss = 0;
- ia64_psr(pt)->tb = 0;
-
- wake_up_process(child);
- ret = 0;
- goto out_tsk;
-
- case PTRACE_KILL:
- /*
- * Make the child exit. Best I can do is send it a
- * sigkill. Perhaps it should be put in the status
- * that it wants to exit.
- */
- if (child->exit_state == EXIT_ZOMBIE)
- /* already dead */
- goto out_tsk;
- child->exit_code = SIGKILL;
-
- ptrace_disable(child);
- wake_up_process(child);
- ret = 0;
- goto out_tsk;
-
- case PTRACE_SINGLESTEP:
- /* let child execute for one instruction */
- case PTRACE_SINGLEBLOCK:
- ret = -EIO;
- if (!valid_signal(data))
- goto out_tsk;
-
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- set_tsk_thread_flag(child, TIF_SINGLESTEP);
- if (request == PTRACE_SINGLESTEP) {
- ia64_psr(pt)->ss = 1;
- } else {
- ia64_psr(pt)->tb = 1;
- }
- child->exit_code = data;
-
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- goto out_tsk;
-
- case PTRACE_DETACH:
- /* detach a process that was attached. */
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- ret = ptrace_detach(child, data);
- goto out_tsk;
-
- case PTRACE_GETREGS:
- ret = ptrace_getregs(child,
- (struct pt_all_user_regs __user *) data);
- goto out_tsk;
-
- case PTRACE_SETREGS:
- ret = ptrace_setregs(child,
- (struct pt_all_user_regs __user *) data);
- goto out_tsk;
-
- default:
- ret = ptrace_request(child, request, addr, data);
- goto out_tsk;
- }
- out_tsk:
- put_task_struct(child);
- out:
- unlock_kernel();
- return ret;
-}
-#endif
/* "asmlinkage" so the input arguments are preserved... */
@@ -1667,6 +770,9 @@ syscall_trace_leave (long arg0, long arg
}
}
+
+#ifdef CONFIG_UTRACE
+
/* Utrace implementation starts here */
typedef struct utrace_get {
@@ -2454,3 +1560,119 @@ const struct utrace_regset_view *utrace_
#endif
return &utrace_ia64_native;
}
+#endif /* CONFIG_UTRACE */
+
+
+#ifdef CONFIG_PTRACE
+
+#define WORD(member, num) \
+ offsetof(struct pt_all_user_regs, member), \
+ offsetof(struct pt_all_user_regs, member) + num * sizeof(long)
+static const struct ptrace_layout_segment pt_all_user_regs_layout[] = {
+ {WORD(nat, 1), 0, ELF_NAT_OFFSET},
+ {WORD(cr_iip, 1), 0, ELF_CR_IIP_OFFSET},
+ {WORD(cfm, 1), 0, ELF_CFM_OFFSET},
+ {WORD(cr_ipsr, 1), 0, ELF_CR_IPSR_OFFSET},
+ {WORD(pr, 1), 0, ELF_PR_OFFSET},
+ {WORD(gr[0], 1), -1, -1},
+ {WORD(gr[1], 31), 0, ELF_GR_OFFSET(1)},
+ {WORD(br[0], 8), 0, ELF_BR_OFFSET(0)},
+ {WORD(ar[0], 16), -1, -1},
+ {WORD(ar[PT_AUR_RSC], 4), 0, ELF_AR_RSC_OFFSET},
+ {WORD(ar[PT_AUR_RNAT+1], 12), -1, -1},
+ {WORD(ar[PT_AUR_CCV], 1), 0, ELF_AR_CCV_OFFSET},
+ {WORD(ar[PT_AUR_CCV+1], 3), -1, -1},
+ {WORD(ar[PT_AUR_UNAT], 1), 0, ELF_AR_UNAT_OFFSET},
+ {WORD(ar[PT_AUR_UNAT+1], 3), -1, -1},
+ {WORD(ar[PT_AUR_FPSR], 1), 0, ELF_AR_FPSR_OFFSET},
+ {WORD(ar[PT_AUR_FPSR+1], 23), -1, -1},
+ {WORD(ar[PT_AUR_PFS], 3), 0, ELF_AR_PFS_OFFSET},
+ {WORD(ar[PT_AUR_EC+1], 62), -1, -1},
+ {offsetof(struct pt_all_user_regs, fr[0]),
+ offsetof(struct pt_all_user_regs, fr[2]),
+ -1, -1},
+ {offsetof(struct pt_all_user_regs, fr[2]),
+ offsetof(struct pt_all_user_regs, fr[128]),
+ 1, 2 * sizeof(elf_fpreg_t)},
+ {0, 0, -1, 0}
+};
+#undef WORD
+
+#define NEXT(addr, sum) (addr + sum * sizeof(long))
+static const struct ptrace_layout_segment pt_uarea_layout[] = {
+ {PT_F32, PT_NAT_BITS, 1, ELF_FP_OFFSET(32)},
+ {PT_NAT_BITS, NEXT(PT_NAT_BITS, 1), 0, ELF_NAT_OFFSET},
+ {PT_F2, PT_F10, 1, ELF_FP_OFFSET(2)},
+ {PT_F10, PT_R4, 1, ELF_FP_OFFSET(10)},
+ {PT_R4, PT_B1, 0, ELF_GR_OFFSET(4)},
+ {PT_B1, PT_AR_EC, 0, ELF_BR_OFFSET(1)},
+ {PT_AR_EC, PT_AR_LC, 0, ELF_AR_EC_OFFSET},
+ {PT_AR_LC, NEXT(PT_AR_LC, 1), 0, ELF_AR_LC_OFFSET},
+ {PT_CR_IPSR, PT_CR_IIP, 0, ELF_CR_IPSR_OFFSET},
+ {PT_CR_IIP, PT_AR_UNAT, 0, ELF_CR_IIP_OFFSET},
+ {PT_AR_UNAT, PT_AR_PFS, 0, ELF_AR_UNAT_OFFSET},
+ {PT_AR_PFS, PT_AR_RSC, 0, ELF_AR_PFS_OFFSET},
+ {PT_AR_RSC, PT_AR_RNAT, 0, ELF_AR_RSC_OFFSET},
+ {PT_AR_RNAT, PT_AR_BSPSTORE, 0, ELF_AR_RNAT_OFFSET},
+ {PT_AR_BSPSTORE,PT_PR, 0, ELF_AR_BSPSTORE_OFFSET},
+ {PT_PR, PT_B6, 0, ELF_PR_OFFSET},
+ {PT_B6, PT_AR_BSP, 0, ELF_BR_OFFSET(6)},
+ {PT_AR_BSP, PT_R1, 0, ELF_AR_BSP_OFFSET},
+ {PT_R1, PT_R12, 0, ELF_GR_OFFSET(1)},
+ {PT_R12, PT_R8, 0, ELF_GR_OFFSET(12)},
+ {PT_R8, PT_R16, 0, ELF_GR_OFFSET(8)},
+ {PT_R16, PT_AR_CCV, 0, ELF_GR_OFFSET(16)},
+ {PT_AR_CCV, PT_AR_FPSR, 0, ELF_AR_CCV_OFFSET},
+ {PT_AR_FPSR, PT_B0, 0, ELF_AR_FPSR_OFFSET},
+ {PT_B0, PT_B7, 0, ELF_BR_OFFSET(0)},
+ {PT_B7, PT_F6, 0, ELF_BR_OFFSET(7)},
+ {PT_F6, PT_AR_CSD, 1, ELF_FP_OFFSET(6)},
+ {PT_AR_CSD, NEXT(PT_AR_CSD, 2), 0, ELF_AR_CSD_OFFSET},
+ {PT_DBR, NEXT(PT_DBR, 8), 2, 0},
+ {PT_IBR, NEXT(PT_IBR, 8), 2, 8 * sizeof(long)},
+ {0, 0, -1, 0}
+};
+#undef NEXT
+
+int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ int ret = -ENOSYS;
+ switch (*request) {
+ case PTRACE_OLD_GETSIGINFO:
+ *request = PTRACE_GETSIGINFO;
+ break;
+ case PTRACE_OLD_SETSIGINFO:
+ *request = PTRACE_SETSIGINFO;
+ break;
+
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA:
+ ret = access_process_vm(child, addr, val, sizeof(*val), 0);
+ ret = ret == sizeof(*val) ? 0 : -EIO;
+ break;
+
+ case PTRACE_PEEKUSR:
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ pt_uarea_layout,
+ addr, sizeof(long),
+ NULL, val, 0);
+ case PTRACE_POKEUSR:
+ return ptrace_pokeusr(child, engine,
+ pt_uarea_layout, addr, data);
+
+ case PTRACE_GETREGS:
+ case PTRACE_SETREGS:
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ pt_all_user_regs_layout,
+ 0, sizeof(struct pt_all_user_regs),
+ (void __user *) data, NULL,
+ *request == PTRACE_SETREGS);
+ }
+ return ret;
+}
+
+#endif /* CONFIG_PTRACE */
linux-2.6-utrace-ptrace-compat-s390.patch:
compat_wrapper.S | 2
ptrace.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 152 insertions(+), 1 deletion(-)
--- NEW FILE linux-2.6-utrace-ptrace-compat-s390.patch ---
[PATCH 4c] utrace: s390 ptrace compatibility
This patch implements ptrace compatibility for s390.
Signed-off-by: Roland McGrath <roland at redhat.com>
Signed-off-by: David Wilder <dwilder at us.ibm.com>
---
arch/s390/kernel/ptrace.c | 151 +++++++++++++++++++++++++++++++++++++
arch/s390/kernel/compat_wrapper.S | 2
2 files changed, 152 insertions(+), 1 deletions(-)
--- linux-2.6/arch/s390/kernel/ptrace.c
+++ linux-2.6/arch/s390/kernel/ptrace.c
@@ -575,6 +575,157 @@ const struct utrace_regset_view *utrace_
}
+#ifdef CONFIG_PTRACE
+static const struct ptrace_layout_segment s390_uarea[] = {
+ {PT_PSWMASK, PT_FPC, 0, 0},
+ {PT_FPC, PT_CR_9, 1, 0},
+ {PT_CR_9, PT_IEEE_IP, 2, 0},
+ {PT_IEEE_IP, sizeof(struct user), -1, -1},
+ {0, 0, -1, 0}
+};
+
+int arch_ptrace(long *request, struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ unsigned long addr, unsigned long data, long *val)
+{
+ ptrace_area parea;
+ unsigned long tmp;
+ int copied;
+
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+#ifdef CONFIG_64BIT
+ /*
+ * Stupid gdb peeks/pokes the access registers in 64 bit with
+ * an alignment of 4. Programmers from hell...
+ */
+ if (addr >= PT_ACR0 && addr < PT_ACR15) {
+ if (addr & 3)
+ return -EIO;
+ tmp = *(unsigned long *)
+ ((char *) child->thread.acrs + addr - PT_ACR0);
+ return put_user(tmp, (unsigned long __user *) data);
+ }
+ else if (addr == PT_ACR15) {
+ /*
+ * Very special case: old & broken 64 bit gdb reading
+ * from acrs[15]. Result is a 64 bit value. Read the
+ * 32 bit acrs[15] value and shift it by 32. Sick...
+ */
+ tmp = ((unsigned long) child->thread.acrs[15]) << 32;
+ return put_user(tmp, (unsigned long __user *) data);
+ }
+#endif
+ return ptrace_peekusr(child, engine, s390_uarea, addr, data);
+ case PTRACE_POKEUSR:
+#ifdef CONFIG_64BIT
+ if (addr >= PT_ACR0 && addr < PT_ACR15) {
+ if (addr & 3)
+ return -EIO;
+ *(unsigned long *) ((char *) child->thread.acrs
+ + addr - PT_ACR0) = data;
+ return 0;
+ }
+ else if (addr == PT_ACR15) {
+ /*
+ * Very special case: old & broken 64 bit gdb writing
+ * to acrs[15] with a 64 bit value. Ignore the lower
+ * half of the value and write the upper 32 bit to
+ * acrs[15]. Sick...
+ */
+ child->thread.acrs[15] = data >> 32;
+ return 0;
+ }
+#endif
+ return ptrace_pokeusr(child, engine, s390_uarea, addr, data);
+
+ case PTRACE_PEEKUSR_AREA:
+ case PTRACE_POKEUSR_AREA:
+ if (copy_from_user(&parea, (ptrace_area __user *) addr,
+ sizeof(parea)))
+ return -EFAULT;
+ if ((parea.kernel_addr | parea.len) & (sizeof(data) - 1))
+ return -EIO;
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ s390_uarea,
+ parea.kernel_addr, parea.len,
+ (void __user *) parea.process_addr,
+ NULL,
+ *request == PTRACE_POKEUSR_AREA);
+
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ /* Remove high order bit from address (only for 31 bit). */
+ addr &= PSW_ADDR_INSN;
+ /* read word at location addr. */
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ if (copied != sizeof(tmp))
+ return -EIO;
+ return put_user(tmp, (unsigned long __user *) data);
+
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ /* Remove high order bit from address (only for 31 bit). */
+ addr &= PSW_ADDR_INSN;
+ /* write the word at location addr. */
+ copied = access_process_vm(child, addr, &data, sizeof(data),1);
+ if (copied != sizeof(data))
+ return -EIO;
+ return 0;
+ }
+
+ return -ENOSYS;
+}
+
+#ifdef CONFIG_COMPAT
+static const struct ptrace_layout_segment s390_compat_uarea[] = {
+ {PT_PSWMASK / 2, PT_FPC / 2, 0, 0},
+ {PT_FPC / 2, PT_CR_9 / 2, 1, 0},
+ {PT_CR_9 / 2, PT_IEEE_IP / 2, 2, 0},
+ {PT_IEEE_IP / 2, sizeof(struct user32), -1, -1},
+ {0, 0, -1, 0}
+};
+
+int arch_compat_ptrace(compat_long_t *request,
+ struct task_struct *child,
+ struct utrace_attached_engine *engine,
+ compat_ulong_t addr, compat_ulong_t data,
+ compat_long_t *val)
+{
+ ptrace_area_emu31 parea;
+
+ switch (*request) {
+ case PTRACE_PEEKUSR:
+ return ptrace_compat_peekusr(child, engine, s390_compat_uarea,
+ addr, data);
+ case PTRACE_POKEUSR:
+ return ptrace_compat_pokeusr(child, engine, s390_compat_uarea,
+ addr, data);
+ case PTRACE_PEEKUSR_AREA:
+ case PTRACE_POKEUSR_AREA:
+ if (copy_from_user(&parea, ((ptrace_area_emu31 __user *)
+ (unsigned long) addr),
+ sizeof(parea)))
+ return -EFAULT;
+ if ((parea.kernel_addr | parea.len) & (sizeof(data) - 1))
+ return -EIO;
+ return ptrace_layout_access(child, engine,
+ utrace_native_view(current),
+ s390_compat_uarea,
+ parea.kernel_addr, parea.len,
+ (void __user *)
+ (unsigned long) parea.process_addr,
+ NULL,
+ *request == PTRACE_POKEUSR_AREA);
+ }
+
+ return -ENOSYS;
+}
+#endif /* CONFIG_COMPAT */
+#endif /* CONFIG_PTRACE */
+
+
asmlinkage void
syscall_trace(struct pt_regs *regs, int entryexit)
{
--- linux-2.6/arch/s390/kernel/compat_wrapper.S
+++ linux-2.6/arch/s390/kernel/compat_wrapper.S
@@ -121,7 +121,7 @@ sys32_ptrace_wrapper:
lgfr %r3,%r3 # long
llgtr %r4,%r4 # long
llgfr %r5,%r5 # long
- jg sys_ptrace # branch to system call
+ jg compat_sys_ptrace # branch to system call
.globl sys32_alarm_wrapper
sys32_alarm_wrapper:
linux-2.6-utrace-ptrace-compat-sparc64.patch:
ptrace.c | 566 +++++++++++----------------------------------------------------
1 files changed, 100 insertions(+), 466 deletions(-)
Index: linux-2.6-utrace-ptrace-compat-sparc64.patch
===================================================================
RCS file: linux-2.6-utrace-ptrace-compat-sparc64.patch
diff -N linux-2.6-utrace-ptrace-compat-sparc64.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-ptrace-compat-sparc64.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,599 @@
+[PATCH 4b] utrace: sparc64 ptrace compatibility
+
+This patch implements ptrace compatibility for sparc64.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+
+---
+
+ arch/sparc64/kernel/ptrace.c | 566 +++++++-----------------------------------
+ 1 files changed, 100 insertions(+), 466 deletions(-)
+
+--- linux-2.6/arch/sparc64/kernel/ptrace.c
++++ linux-2.6/arch/sparc64/kernel/ptrace.c
+@@ -667,484 +667,118 @@ void flush_ptrace_access(struct vm_area_
+ }
+ }
+
+-#if 0 /* XXX */
+-asmlinkage void do_ptrace(struct pt_regs *regs)
++#ifdef CONFIG_PTRACE
++static const struct ptrace_layout_segment sparc64_getregs_layout[] = {
++ { 0, offsetof(struct pt_regs, u_regs[15]), 0, sizeof(long) },
++ { offsetof(struct pt_regs, u_regs[15]),
++ offsetof(struct pt_regs, tstate),
++ -1, 0 },
++ { offsetof(struct pt_regs, tstate), offsetof(struct pt_regs, y),
++ 0, 32 * sizeof(long) },
++ {0, 0, -1, 0}
++};
++
++int arch_ptrace(long *request, struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ unsigned long addr, unsigned long data,
++ long *retval)
+ {
+- int request = regs->u_regs[UREG_I0];
+- pid_t pid = regs->u_regs[UREG_I1];
+- unsigned long addr = regs->u_regs[UREG_I2];
+- unsigned long data = regs->u_regs[UREG_I3];
+- unsigned long addr2 = regs->u_regs[UREG_I4];
+- struct task_struct *child;
+- int ret;
+-
+- if (test_thread_flag(TIF_32BIT)) {
+- addr &= 0xffffffffUL;
+- data &= 0xffffffffUL;
+- addr2 &= 0xffffffffUL;
+- }
+- lock_kernel();
+-#ifdef DEBUG_PTRACE
+- {
+- char *s;
+-
+- if ((request >= 0) && (request <= 24))
+- s = pt_rq [request];
+- else
+- s = "unknown";
+-
+- if (request == PTRACE_POKEDATA && data == 0x91d02001){
+- printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n",
+- pid, addr, addr2);
+- } else
+- printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n",
+- s, request, pid, addr, data, addr2);
+- }
+-#endif
+- if (request == PTRACE_TRACEME) {
+- ret = ptrace_traceme();
+- if (ret < 0)
+- pt_error_return(regs, -ret);
+- else
+- pt_succ_return(regs, 0);
+- goto out;
+- }
+-
+- child = ptrace_get_task_struct(pid);
+- if (IS_ERR(child)) {
+- ret = PTR_ERR(child);
+- pt_error_return(regs, -ret);
+- goto out;
+- }
+-
+- if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
+- || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
+- if (ptrace_attach(child)) {
+- pt_error_return(regs, EPERM);
+- goto out_tsk;
+- }
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+-
+- ret = ptrace_check_attach(child, request == PTRACE_KILL);
+- if (ret < 0) {
+- pt_error_return(regs, -ret);
+- goto out_tsk;
+- }
+-
+- if (!(test_thread_flag(TIF_32BIT)) &&
+- ((request == PTRACE_READDATA64) ||
+- (request == PTRACE_WRITEDATA64) ||
+- (request == PTRACE_READTEXT64) ||
+- (request == PTRACE_WRITETEXT64) ||
+- (request == PTRACE_PEEKTEXT64) ||
+- (request == PTRACE_POKETEXT64) ||
+- (request == PTRACE_PEEKDATA64) ||
+- (request == PTRACE_POKEDATA64))) {
+- addr = regs->u_regs[UREG_G2];
+- addr2 = regs->u_regs[UREG_G3];
+- request -= 30; /* wheee... */
+- }
+-
+- switch(request) {
+- case PTRACE_PEEKUSR:
+- if (addr != 0)
+- pt_error_return(regs, EIO);
+- else
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+-
+- case PTRACE_PEEKTEXT: /* read word at location addr. */
+- case PTRACE_PEEKDATA: {
+- unsigned long tmp64;
+- unsigned int tmp32;
+- int res, copied;
+-
+- res = -EIO;
+- if (test_thread_flag(TIF_32BIT)) {
+- copied = access_process_vm(child, addr,
+- &tmp32, sizeof(tmp32), 0);
+- tmp64 = (unsigned long) tmp32;
+- if (copied == sizeof(tmp32))
+- res = 0;
+- } else {
+- copied = access_process_vm(child, addr,
+- &tmp64, sizeof(tmp64), 0);
+- if (copied == sizeof(tmp64))
+- res = 0;
+- }
+- if (res < 0)
+- pt_error_return(regs, -res);
+- else
+- pt_os_succ_return(regs, tmp64, (void __user *) data);
+- goto out_tsk;
+- }
+-
+- case PTRACE_POKETEXT: /* write the word at location addr. */
+- case PTRACE_POKEDATA: {
+- unsigned long tmp64;
+- unsigned int tmp32;
+- int copied, res = -EIO;
+-
+- if (test_thread_flag(TIF_32BIT)) {
+- tmp32 = data;
+- copied = access_process_vm(child, addr,
+- &tmp32, sizeof(tmp32), 1);
+- if (copied == sizeof(tmp32))
+- res = 0;
+- } else {
+- tmp64 = data;
+- copied = access_process_vm(child, addr,
+- &tmp64, sizeof(tmp64), 1);
+- if (copied == sizeof(tmp64))
+- res = 0;
+- }
+- if (res < 0)
+- pt_error_return(regs, -res);
+- else
+- pt_succ_return(regs, res);
+- goto out_tsk;
+- }
+-
+- case PTRACE_GETREGS: {
+- struct pt_regs32 __user *pregs =
+- (struct pt_regs32 __user *) addr;
+- struct pt_regs *cregs = task_pt_regs(child);
+- int rval;
+-
+- if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) ||
+- __put_user(cregs->tpc, (&pregs->pc)) ||
+- __put_user(cregs->tnpc, (&pregs->npc)) ||
+- __put_user(cregs->y, (&pregs->y))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- for (rval = 1; rval < 16; rval++)
+- if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- pt_succ_return(regs, 0);
+-#ifdef DEBUG_PTRACE
+- printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
+-#endif
+- goto out_tsk;
+- }
+-
+- case PTRACE_GETREGS64: {
+- struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
+- struct pt_regs *cregs = task_pt_regs(child);
+- unsigned long tpc = cregs->tpc;
+- int rval;
+-
+- if ((task_thread_info(child)->flags & _TIF_32BIT) != 0)
+- tpc &= 0xffffffff;
+- if (__put_user(cregs->tstate, (&pregs->tstate)) ||
+- __put_user(tpc, (&pregs->tpc)) ||
+- __put_user(cregs->tnpc, (&pregs->tnpc)) ||
+- __put_user(cregs->y, (&pregs->y))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- for (rval = 1; rval < 16; rval++)
+- if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- pt_succ_return(regs, 0);
+-#ifdef DEBUG_PTRACE
+- printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
+-#endif
+- goto out_tsk;
+- }
+-
+- case PTRACE_SETREGS: {
+- struct pt_regs32 __user *pregs =
+- (struct pt_regs32 __user *) addr;
+- struct pt_regs *cregs = task_pt_regs(child);
+- unsigned int psr, pc, npc, y;
+- int i;
+-
+- /* Must be careful, tracing process can only set certain
+- * bits in the psr.
+- */
+- if (__get_user(psr, (&pregs->psr)) ||
+- __get_user(pc, (&pregs->pc)) ||
+- __get_user(npc, (&pregs->npc)) ||
+- __get_user(y, (&pregs->y))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- cregs->tstate &= ~(TSTATE_ICC);
+- cregs->tstate |= psr_to_tstate_icc(psr);
+- if (!((pc | npc) & 3)) {
+- cregs->tpc = pc;
+- cregs->tnpc = npc;
+- }
+- cregs->y = y;
+- for (i = 1; i < 16; i++) {
+- if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- }
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+-
+- case PTRACE_SETREGS64: {
+- struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
+- struct pt_regs *cregs = task_pt_regs(child);
+- unsigned long tstate, tpc, tnpc, y;
+- int i;
+-
+- /* Must be careful, tracing process can only set certain
+- * bits in the psr.
+- */
+- if (__get_user(tstate, (&pregs->tstate)) ||
+- __get_user(tpc, (&pregs->tpc)) ||
+- __get_user(tnpc, (&pregs->tnpc)) ||
+- __get_user(y, (&pregs->y))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) {
+- tpc &= 0xffffffff;
+- tnpc &= 0xffffffff;
+- }
+- tstate &= (TSTATE_ICC | TSTATE_XCC);
+- cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
+- cregs->tstate |= tstate;
+- if (!((tpc | tnpc) & 3)) {
+- cregs->tpc = tpc;
+- cregs->tnpc = tnpc;
+- }
+- cregs->y = y;
+- for (i = 1; i < 16; i++) {
+- if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- }
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+-
+- case PTRACE_GETFPREGS: {
+- struct fps {
+- unsigned int regs[32];
+- unsigned int fsr;
+- unsigned int flags;
+- unsigned int extra;
+- unsigned int fpqd;
+- struct fq {
+- unsigned int insnaddr;
+- unsigned int insn;
+- } fpq[16];
+- };
+- struct fps __user *fps = (struct fps __user *) addr;
+- unsigned long *fpregs = task_thread_info(child)->fpregs;
+-
+- if (copy_to_user(&fps->regs[0], fpregs,
+- (32 * sizeof(unsigned int))) ||
+- __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr)) ||
+- __put_user(0, (&fps->fpqd)) ||
+- __put_user(0, (&fps->flags)) ||
+- __put_user(0, (&fps->extra)) ||
+- clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+-
+- case PTRACE_GETFPREGS64: {
+- struct fps {
+- unsigned int regs[64];
+- unsigned long fsr;
+- };
+- struct fps __user *fps = (struct fps __user *) addr;
+- unsigned long *fpregs = task_thread_info(child)->fpregs;
+-
+- if (copy_to_user(&fps->regs[0], fpregs,
+- (64 * sizeof(unsigned int))) ||
+- __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+-
+- case PTRACE_SETFPREGS: {
+- struct fps {
+- unsigned int regs[32];
+- unsigned int fsr;
+- unsigned int flags;
+- unsigned int extra;
+- unsigned int fpqd;
+- struct fq {
+- unsigned int insnaddr;
+- unsigned int insn;
+- } fpq[16];
+- };
+- struct fps __user *fps = (struct fps __user *) addr;
+- unsigned long *fpregs = task_thread_info(child)->fpregs;
+- unsigned fsr;
+-
+- if (copy_from_user(fpregs, &fps->regs[0],
+- (32 * sizeof(unsigned int))) ||
+- __get_user(fsr, (&fps->fsr))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- task_thread_info(child)->xfsr[0] &= 0xffffffff00000000UL;
+- task_thread_info(child)->xfsr[0] |= fsr;
+- if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF))
+- task_thread_info(child)->gsr[0] = 0;
+- task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL);
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+-
+- case PTRACE_SETFPREGS64: {
+- struct fps {
+- unsigned int regs[64];
+- unsigned long fsr;
+- };
+- struct fps __user *fps = (struct fps __user *) addr;
+- unsigned long *fpregs = task_thread_info(child)->fpregs;
+-
+- if (copy_from_user(fpregs, &fps->regs[0],
+- (64 * sizeof(unsigned int))) ||
+- __get_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) {
+- pt_error_return(regs, EFAULT);
+- goto out_tsk;
+- }
+- if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF))
+- task_thread_info(child)->gsr[0] = 0;
+- task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU);
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
++ void __user *uaddr = (void __user *) addr;
++ struct pt_regs *uregs = uaddr;
++ int err = -ENOSYS;
++
++ switch (*request) {
++ case PTRACE_GETREGS64:
++ err = ptrace_layout_access(child, engine,
++ &utrace_sparc64_native_view,
++ sparc64_getregs_layout,
++ 0, offsetof(struct pt_regs, y),
++ uaddr, NULL, 0);
++ if (!err &&
++ (put_user(task_pt_regs(child)->y, &uregs->y) ||
++ put_user(task_pt_regs(child)->fprs, &uregs->fprs)))
++ err = -EFAULT;
++ break;
+
+- case PTRACE_READTEXT:
+- case PTRACE_READDATA: {
+- int res = ptrace_readdata(child, addr,
+- (char __user *)addr2, data);
+- if (res == data) {
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+- if (res >= 0)
+- res = -EIO;
+- pt_error_return(regs, -res);
+- goto out_tsk;
+- }
++ case PTRACE_SETREGS64:
++ err = ptrace_layout_access(child, engine,
++ &utrace_sparc64_native_view,
++ sparc64_getregs_layout,
++ 0, offsetof(struct pt_regs, y),
++ uaddr, NULL, 1);
++ if (!err &&
++ (get_user(task_pt_regs(child)->y, &uregs->y) ||
++ get_user(task_pt_regs(child)->fprs, &uregs->fprs)))
++ err = -EFAULT;
++ break;
+
+- case PTRACE_WRITETEXT:
+- case PTRACE_WRITEDATA: {
+- int res = ptrace_writedata(child, (char __user *) addr2,
+- addr, data);
+- if (res == data) {
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+- if (res >= 0)
+- res = -EIO;
+- pt_error_return(regs, -res);
+- goto out_tsk;
+- }
+- case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
+- addr = 1;
++ case PTRACE_GETFPREGS64:
++ case PTRACE_SETFPREGS64:
++ err = ptrace_regset_access(child, engine,
++ utrace_native_view(current),
++ 2, 0, 34 * sizeof(long), uaddr,
++ (*request == PTRACE_SETFPREGS64));
++ break;
+
+- case PTRACE_CONT: { /* restart after signal. */
+- if (!valid_signal(data)) {
+- pt_error_return(regs, EIO);
+- goto out_tsk;
+- }
+-
+- if (request == PTRACE_SYSCALL) {
+- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+- } else {
+- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+- }
+-
+- child->exit_code = data;
+-#ifdef DEBUG_PTRACE
+- printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child->comm,
+- child->pid, child->exit_code,
+- task_pt_regs(child)->tpc,
+- task_pt_regs(child)->tnpc);
+-
+-#endif
+- wake_up_process(child);
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
++ case PTRACE_SUNDETACH:
++ *request = PTRACE_DETACH;
++ break;
+
+-/*
+- * make the child exit. Best I can do is send it a sigkill.
+- * perhaps it should be put in the status that it wants to
+- * exit.
+- */
+- case PTRACE_KILL: {
+- if (child->exit_state == EXIT_ZOMBIE) { /* already dead */
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+- child->exit_code = SIGKILL;
+- wake_up_process(child);
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
++ default:
++ break;
++ };
++ return err;
++}
+
+- case PTRACE_SUNDETACH: { /* detach a process that was attached. */
+- int error = ptrace_detach(child, data);
+- if (error) {
+- pt_error_return(regs, EIO);
+- goto out_tsk;
+- }
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
++#ifdef CONFIG_COMPAT
++static const struct ptrace_layout_segment sparc32_getregs_layout[] = {
++ { 0, offsetof(struct pt_regs32, u_regs[0]),
++ 0, GENREG32_PSR * sizeof(u32) },
++ { offsetof(struct pt_regs32, u_regs[0]),
++ offsetof(struct pt_regs32, u_regs[15]),
++ 0, 1 * sizeof(u32) },
++ { offsetof(struct pt_regs32, u_regs[15]), sizeof(struct pt_regs32),
++ -1, 0 },
++ {0, 0, -1, 0}
++};
++
++int arch_compat_ptrace(compat_long_t *request, struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ compat_ulong_t addr, compat_ulong_t data,
++ compat_long_t *retval)
++{
++ void __user *uaddr = (void __user *) (unsigned long) addr;
++ int err = -ENOSYS;
+
+- /* PTRACE_DUMPCORE unsupported... */
++ switch (*request) {
++ case PTRACE_GETREGS:
++ case PTRACE_SETREGS:
++ err = ptrace_layout_access(child, engine,
++ &utrace_sparc32_view,
++ sparc32_getregs_layout,
++ 0, sizeof(struct pt_regs32),
++ uaddr, NULL,
++ (*request ==
++ PTRACE_SETREGS));
++ break;
+
+- case PTRACE_GETEVENTMSG: {
+- int err;
++ case PTRACE_GETFPREGS:
++ case PTRACE_SETFPREGS:
++ err = ptrace_whole_regset(child, engine, addr, 1,
++ (*request == PTRACE_SETFPREGS));
++ break;
+
+- if (test_thread_flag(TIF_32BIT))
+- err = put_user(child->ptrace_message,
+- (unsigned int __user *) data);
+- else
+- err = put_user(child->ptrace_message,
+- (unsigned long __user *) data);
+- if (err)
+- pt_error_return(regs, -err);
+- else
+- pt_succ_return(regs, 0);
++ case PTRACE_SUNDETACH:
++ *request = PTRACE_DETACH;
+ break;
+- }
+
+- default: {
+- int err = ptrace_request(child, request, addr, data);
+- if (err)
+- pt_error_return(regs, -err);
+- else
+- pt_succ_return(regs, 0);
+- goto out_tsk;
+- }
+- }
+-out_tsk:
+- if (child)
+- put_task_struct(child);
+-out:
+- unlock_kernel();
++ default:
++ break;
++ };
++ return err;
+ }
+-#endif
++#endif /* CONFIG_COMPAT */
++#endif /* CONFIG_PTRACE */
+
+ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
+ {
linux-2.6-utrace-ptrace-compat.patch:
arch/i386/kernel/ptrace.c | 40
arch/powerpc/kernel/ptrace.c | 250 ++++
arch/powerpc/kernel/signal_32.c | 52 +
arch/powerpc/lib/sstep.c | 3
arch/x86_64/ia32/ia32entry.S | 2
arch/x86_64/ia32/ptrace32.c | 56 -
arch/x86_64/kernel/ptrace.c | 46
fs/proc/base.c | 40
include/asm-x86_64/ptrace-abi.h | 3
include/asm-x86_64/tracehook.h | 1
include/linux/ptrace.h | 221 +++-
include/linux/sched.h | 4
init/Kconfig | 15
kernel/Makefile | 3
kernel/exit.c | 13
kernel/fork.c | 2
kernel/ptrace.c | 2051 +++++++++++++++++++++++++++++++++++++---
kernel/sys_ni.c | 4
18 files changed, 2632 insertions(+), 174 deletions(-)
View full diff with command:
/usr/bin/cvs -f diff -kk -u -N -r 1.3 -r 1.4 linux-2.6-utrace-ptrace-compat.patch
Index: linux-2.6-utrace-ptrace-compat.patch
===================================================================
RCS file: linux-2.6-utrace-ptrace-compat.patch
diff -N linux-2.6-utrace-ptrace-compat.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-ptrace-compat.patch 19 Jul 2007 17:46:36 -0000 1.4
@@ -0,0 +1,3118 @@
+[PATCH 4] utrace: ptrace compatibility
+
+ptrace compatibility on top of <linux/utrace.h> interfaces.
+This attempts to be precisely compatible with existing ptrace behavior.
+It does not extend, improve, or change it.
+
+The ptrace support is made an option, CONFIG_PTRACE. For now, noone will
+want to turn this off except maybe a bizarre embedded configuration. But
+it looks forward to a day when we can punt the ptrace system call completely.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+
+---
+
+ fs/proc/base.c | 40 +
+ arch/i386/kernel/ptrace.c | 40 +
+ arch/x86_64/ia32/ptrace32.c | 56 +
+ arch/x86_64/ia32/ia32entry.S | 2
+ arch/x86_64/kernel/ptrace.c | 46 +
+ arch/powerpc/kernel/ptrace.c | 250 +++++
+ arch/powerpc/kernel/signal_32.c | 52 +
+ arch/powerpc/lib/sstep.c | 3
+ kernel/fork.c | 2
+ kernel/exit.c | 13
+ kernel/ptrace.c | 2051 +++++++++++++++++++++++++++++++++++++--
+ kernel/sys_ni.c | 4
+ kernel/Makefile | 3
+ include/linux/sched.h | 4
+ include/linux/ptrace.h | 221 +++-
+ include/asm-x86_64/tracehook.h | 1
+ include/asm-x86_64/ptrace-abi.h | 3
+ init/Kconfig | 15
+ 18 files changed, 2632 insertions(+), 174 deletions(-)
+
+--- linux-2.6/fs/proc/base.c
++++ linux-2.6/fs/proc/base.c
+@@ -151,6 +151,46 @@ static int get_nr_threads(struct task_st
+ return count;
+ }
+
++static int __ptrace_may_attach(struct task_struct *task)
++{
++ /* May we inspect the given task?
++ * This check is used both for attaching with ptrace
++ * and for allowing access to sensitive information in /proc.
++ *
++ * ptrace_attach denies several cases that /proc allows
++ * because setting up the necessary parent/child relationship
++ * or halting the specified task is impossible.
++ */
++ int dumpable = 0;
++ /* Don't let security modules deny introspection */
++ if (task == current)
++ return 0;
++ if (((current->uid != task->euid) ||
++ (current->uid != task->suid) ||
++ (current->uid != task->uid) ||
++ (current->gid != task->egid) ||
++ (current->gid != task->sgid) ||
++ (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
++ return -EPERM;
++ smp_rmb();
++ if (task->mm)
++ dumpable = task->mm->dumpable;
++ if (!dumpable && !capable(CAP_SYS_PTRACE))
++ return -EPERM;
++
++ return security_ptrace(current, task);
++}
++
++int ptrace_may_attach(struct task_struct *task)
++{
++ int err;
++ task_lock(task);
++ err = __ptrace_may_attach(task);
++ task_unlock(task);
++ return !err;
++}
++
++
+ static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+ {
+ struct task_struct *task = get_proc_task(inode);
+--- linux-2.6/arch/i386/kernel/ptrace.c
++++ linux-2.6/arch/i386/kernel/ptrace.c
+@@ -735,6 +735,46 @@ const struct utrace_regset_view *utrace_
+ return &utrace_i386_native;
+ }
+
++#ifdef CONFIG_PTRACE
++static const struct ptrace_layout_segment i386_uarea[] = {
++ {0, FRAME_SIZE*4, 0, 0},
++ {FRAME_SIZE*4, offsetof(struct user, u_debugreg[0]), -1, 0},
++ {offsetof(struct user, u_debugreg[0]),
++ offsetof(struct user, u_debugreg[8]), 4, 0},
++ {0, 0, -1, 0}
++};
++
++int arch_ptrace(long *req, struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ unsigned long addr, unsigned long data, long *val)
++{
++ switch (*req) {
++ case PTRACE_PEEKUSR:
++ return ptrace_peekusr(child, engine, i386_uarea, addr, data);
++ case PTRACE_POKEUSR:
++ return ptrace_pokeusr(child, engine, i386_uarea, addr, data);
++ case PTRACE_GETREGS:
++ return ptrace_whole_regset(child, engine, data, 0, 0);
++ case PTRACE_SETREGS:
++ return ptrace_whole_regset(child, engine, data, 0, 1);
++ case PTRACE_GETFPREGS:
++ return ptrace_whole_regset(child, engine, data, 1, 0);
++ case PTRACE_SETFPREGS:
++ return ptrace_whole_regset(child, engine, data, 1, 1);
++ case PTRACE_GETFPXREGS:
++ return ptrace_whole_regset(child, engine, data, 2, 0);
++ case PTRACE_SETFPXREGS:
++ return ptrace_whole_regset(child, engine, data, 2, 1);
++ case PTRACE_GET_THREAD_AREA:
++ case PTRACE_SET_THREAD_AREA:
++ return ptrace_onereg_access(child, engine,
++ utrace_native_view(current), 3,
++ addr, (void __user *)data,
++ *req == PTRACE_SET_THREAD_AREA);
++ }
++ return -ENOSYS;
++}
++#endif
+
+ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
+ {
+--- linux-2.6/arch/x86_64/ia32/ptrace32.c
++++ linux-2.6/arch/x86_64/ia32/ptrace32.c
+@@ -166,11 +166,6 @@ static int getreg32(struct task_struct *
+
+ #undef R32
+
+-asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
+-{
+- return -ENOSYS;
+-}
+-
+ static int
+ ia32_genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+@@ -600,3 +595,54 @@ const struct utrace_regset_view utrace_i
+ .name = "i386", .e_machine = EM_386,
+ .regsets = ia32_regsets, .n = ARRAY_SIZE(ia32_regsets)
+ };
++
++
++#ifdef CONFIG_PTRACE
++/*
++ * This matches the arch/i386/kernel/ptrace.c definitions.
++ */
++
++static const struct ptrace_layout_segment ia32_uarea[] = {
++ {0, sizeof(struct user_regs_struct32), 0, 0},
++ {sizeof(struct user_regs_struct32),
++ offsetof(struct user32, u_debugreg[0]), -1, 0},
++ {offsetof(struct user32, u_debugreg[0]),
++ offsetof(struct user32, u_debugreg[8]), 4, 0},
++ {0, 0, -1, 0}
++};
++
++int arch_compat_ptrace(compat_long_t *req, struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ compat_ulong_t addr, compat_ulong_t data,
++ compat_long_t *val)
++{
++ switch (*req) {
++ case PTRACE_PEEKUSR:
++ return ptrace_compat_peekusr(child, engine, ia32_uarea,
++ addr, data);
++ case PTRACE_POKEUSR:
++ return ptrace_compat_pokeusr(child, engine, ia32_uarea,
++ addr, data);
++ case PTRACE_GETREGS:
++ return ptrace_whole_regset(child, engine, data, 0, 0);
++ case PTRACE_SETREGS:
++ return ptrace_whole_regset(child, engine, data, 0, 1);
++ case PTRACE_GETFPREGS:
++ return ptrace_whole_regset(child, engine, data, 1, 0);
++ case PTRACE_SETFPREGS:
++ return ptrace_whole_regset(child, engine, data, 1, 1);
++ case PTRACE_GETFPXREGS:
++ return ptrace_whole_regset(child, engine, data, 2, 0);
++ case PTRACE_SETFPXREGS:
++ return ptrace_whole_regset(child, engine, data, 2, 1);
++ case PTRACE_GET_THREAD_AREA:
++ case PTRACE_SET_THREAD_AREA:
++ return ptrace_onereg_access(child, engine,
[...2725 lines suppressed...]
++ struct utrace_attached_engine *engine,
++ const struct utrace_regset_view *view,
++ int setno, unsigned long offset,
++ unsigned int size, void __user *data,
++ int write);
++
++/*
++ * Convenience wrapper for doing access to a whole utrace_regset for ptrace.
++ */
++static inline int ptrace_whole_regset(struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ long data, int setno, int write)
++{
++ return ptrace_regset_access(child, engine, utrace_native_view(current),
++ setno, 0, -1, (void __user *)data, write);
++}
++
++/*
++ * Convenience function doing access to a single slot in a utrace_regset.
++ * The regno value gives a slot number plus regset->bias.
++ * The value accessed is regset->size bytes long.
++ */
++extern int ptrace_onereg_access(struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ const struct utrace_regset_view *view,
++ int setno, unsigned long regno,
++ void __user *data, int write);
++
++
++/*
++ * An array of these describes the layout of the virtual struct user
++ * accessed by PEEKUSR/POKEUSR, or the structure used by GETREGS et al.
++ * The array is terminated by an element with .end of zero.
++ * An element describes the range [.start, .end) of struct user offsets,
++ * measured in bytes; it maps to the regset in the view's regsets array
++ * at the index given by .regset, at .offset bytes into that regset's data.
++ * If .regset is -1, then the [.start, .end) range reads as zero
++ * if .offset is zero, and is skipped on read (user's buffer unchanged)
++ * if .offset is -1.
++ */
++struct ptrace_layout_segment {
++ unsigned int start, end, regset, offset;
++};
++
++/*
++ * Convenience function for doing access to a ptrace compatibility layout.
++ * The offset and size are in bytes.
++ */
++extern int ptrace_layout_access(struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ const struct utrace_regset_view *view,
++ const struct ptrace_layout_segment layout[],
++ unsigned long offset, unsigned int size,
++ void __user *data, void *kdata, int write);
++
++
++/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
++static inline int ptrace_peekusr(struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ const struct ptrace_layout_segment layout[],
++ unsigned long addr, long data)
++{
++ return ptrace_layout_access(child, engine, utrace_native_view(current),
++ layout, addr, sizeof(long),
++ (unsigned long __user *)data, NULL, 0);
++}
++
++/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
++static inline int ptrace_pokeusr(struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ const struct ptrace_layout_segment layout[],
++ unsigned long addr, long data)
++{
++ return ptrace_layout_access(child, engine, utrace_native_view(current),
++ layout, addr, sizeof(long),
++ NULL, &data, 1);
++}
++
++/*
++ * Called in copy_process.
++ */
++static inline void ptrace_init_task(struct task_struct *tsk)
++{
++ INIT_LIST_HEAD(&tsk->ptracees);
++}
++
++/*
++ * Called in do_exit, after setting PF_EXITING, no locks are held.
++ */
++void ptrace_exit(struct task_struct *tsk);
++
++/*
++ * Called in do_wait, with tasklist_lock held for reading.
++ * This reports any ptrace-child that is ready as do_wait would a normal child.
++ * If there are no ptrace children, returns -ECHILD.
++ * If there are some ptrace children but none reporting now, returns 0.
++ * In those cases the tasklist_lock is still held so next_thread(tsk) works.
++ * For any other return value, tasklist_lock is released before return.
++ */
++int ptrace_do_wait(struct task_struct *tsk,
++ pid_t pid, int options, struct siginfo __user *infop,
++ int __user *stat_addr, struct rusage __user *rusagep);
++
++
++#ifdef CONFIG_COMPAT
++#include <linux/compat.h>
++
++extern int arch_compat_ptrace(compat_long_t *request,
++ struct task_struct *child,
++ struct utrace_attached_engine *engine,
++ compat_ulong_t a, compat_ulong_t d,
++ compat_long_t *retval);
++
++/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
++static inline int ptrace_compat_peekusr(
++ struct task_struct *child, struct utrace_attached_engine *engine,
++ const struct ptrace_layout_segment layout[],
++ compat_ulong_t addr, compat_ulong_t data)
++{
++ compat_ulong_t *udata = (compat_ulong_t __user *) (unsigned long) data;
++ return ptrace_layout_access(child, engine, utrace_native_view(current),
++ layout, addr, sizeof(compat_ulong_t),
++ udata, NULL, 0);
++}
++
++/* Convenience wrapper for the common PTRACE_PEEKUSR implementation. */
++static inline int ptrace_compat_pokeusr(
++ struct task_struct *child, struct utrace_attached_engine *engine,
++ const struct ptrace_layout_segment layout[],
++ compat_ulong_t addr, compat_ulong_t data)
++{
++ return ptrace_layout_access(child, engine, utrace_native_view(current),
++ layout, addr, sizeof(compat_ulong_t),
++ NULL, &data, 1);
++}
++#endif /* CONFIG_COMPAT */
++
++#else /* no CONFIG_PTRACE */
++static inline void ptrace_init_task(struct task_struct *tsk) { }
++static inline void ptrace_exit(struct task_struct *tsk) { }
++static inline int ptrace_do_wait(struct task_struct *tsk,
++ pid_t pid, int options,
++ struct siginfo __user *infop,
++ int __user *stat_addr,
++ struct rusage __user *rusagep)
++{
++ return -ECHILD;
++}
++#endif /* CONFIG_PTRACE */
++
++
+ #ifndef force_successful_syscall_return
+ /*
+ * System call handlers that, upon successful completion, need to return a
+--- linux-2.6/include/asm-x86_64/tracehook.h
++++ linux-2.6/include/asm-x86_64/tracehook.h
+@@ -15,6 +15,7 @@
+
+ #include <linux/sched.h>
+ #include <asm/ptrace.h>
++#include <asm/proto.h>
+
+ /*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+--- linux-2.6/include/asm-x86_64/ptrace-abi.h
++++ linux-2.6/include/asm-x86_64/ptrace-abi.h
+@@ -48,4 +48,7 @@
+
+ #define PTRACE_ARCH_PRCTL 30 /* arch_prctl for child */
+
++#define PTRACE_SYSEMU 31
++#define PTRACE_SYSEMU_SINGLESTEP 32
++
+ #endif
+--- linux-2.6/init/Kconfig
++++ linux-2.6/init/Kconfig
+@@ -692,10 +692,21 @@ endmenu
+
+ menu "Process debugging support"
+
++config PTRACE
++ bool "Legacy ptrace system call interface"
++ default y
++ select UTRACE
++ depends on PROC_FS
++ help
++ Enable the ptrace system call.
++ This is traditionally used by debuggers like GDB,
++ and is used by UML and some other applications.
++ Unless you are very sure you won't run anything that needs it,
++ say Y.
++
+ config UTRACE
+ bool "Infrastructure for tracing and debugging user processes"
+- default y
+- depends on MODULES
++ default y if MODULES || PTRACE
+ help
+ Enable the utrace process tracing interface.
+ This is an internal kernel interface to track events in user
linux-2.6-utrace-regset-avr32.patch:
ptrace.c | 108 +++++++++++++++++++++++++--------------------------------------
1 files changed, 43 insertions(+), 65 deletions(-)
--- NEW FILE linux-2.6-utrace-regset-avr32.patch ---
[PATCH 2d] utrace: avr32 regset support
From: Haavard Skinnemoen <hskinnemoen at atmel.com>
Rip out most of the ptrace code for AVR32 and replace it with the much
nicer utrace stuff.
CC: Haavard Skinnemoen <hskinnemoen at atmel.com>
Signed-off-by: Roland McGrath <roland at redhat.com>
---
arch/avr32/kernel/ptrace.c | 108 ++++++++++++++++++--------------------------
1 files changed, 43 insertions(+), 65 deletions(-)
--- linux-2.6/arch/avr32/kernel/ptrace.c
+++ linux-2.6/arch/avr32/kernel/ptrace.c
@@ -14,94 +14,72 @@
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/user.h>
+#include <linux/tracehook.h>
+#include <asm/tracehook.h>
#include <asm/mmu_context.h>
#include <linux/kdebug.h>
+#ifdef CONFIG_UTRACE
+
static struct pt_regs *get_user_regs(struct task_struct *tsk)
{
return (struct pt_regs *)((unsigned long)task_stack_page(tsk) +
THREAD_SIZE - sizeof(struct pt_regs));
}
-#if 0
-/*
- * Read the word at offset "offset" into the task's "struct user". We
- * actually access the pt_regs struct stored on the kernel stack.
- */
-static int ptrace_read_user(struct task_struct *tsk, unsigned long offset,
- unsigned long __user *data)
+static int genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- unsigned long *regs;
- unsigned long value;
-
- pr_debug("ptrace_read_user(%p, %#lx, %p)\n",
- tsk, offset, data);
-
- if (offset & 3 || offset >= sizeof(struct user)) {
- printk("ptrace_read_user: invalid offset 0x%08lx\n", offset);
- return -EIO;
- }
+ struct pt_regs *regs = get_user_regs(target);
- regs = (unsigned long *)get_user_regs(tsk);
-
- value = 0;
- if (offset < sizeof(struct pt_regs))
- value = regs[offset / sizeof(regs[0])];
-
- return put_user(value, data);
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ regs, 0, -1);
}
-/*
- * Write the word "value" to offset "offset" into the task's "struct
- * user". We actually access the pt_regs struct stored on the kernel
- * stack.
- */
-static int ptrace_write_user(struct task_struct *tsk, unsigned long offset,
- unsigned long value)
+static int genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- unsigned long *regs;
-
- if (offset & 3 || offset >= sizeof(struct user)) {
- printk("ptrace_write_user: invalid offset 0x%08lx\n", offset);
- return -EIO;
- }
-
- if (offset >= sizeof(struct pt_regs))
- return 0;
+ struct pt_regs *regs = get_user_regs(target);
- regs = (unsigned long *)get_user_regs(tsk);
- regs[offset / sizeof(regs[0])] = value;
-
- return 0;
+ return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ regs, 0, -1);
}
-static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
-{
- struct pt_regs *regs = get_user_regs(tsk);
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = ELF_NGREG,
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = genregs_get,
+ .set = genregs_set,
+ },
+ /*
+ * Other register sets that probably would make sense:
+ * - Coprocessor registers (8 coprocs with 16 registers each)
+ * - TLS stuff
+ */
+};
- return copy_to_user(uregs, regs, sizeof(*regs)) ? -EFAULT : 0;
-}
+static const struct utrace_regset_view utrace_avr32_native_view = {
+ .name = UTS_MACHINE,
+ .e_machine = ELF_ARCH,
+ .regsets = native_regsets,
+ .n = ARRAY_SIZE(native_regsets),
+};
-static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs)
+const struct utrace_regset_view *utrace_native_view(struct task_struct *tsk)
{
- struct pt_regs newregs;
- int ret;
-
- ret = -EFAULT;
- if (copy_from_user(&newregs, uregs, sizeof(newregs)) == 0) {
- struct pt_regs *regs = get_user_regs(tsk);
-
- ret = -EINVAL;
- if (valid_user_regs(&newregs)) {
- *regs = newregs;
- ret = 0;
- }
- }
-
- return ret;
+ return &utrace_avr32_native_view;
}
+#endif /* CONFIG_UTRACE */
+
+#if 0
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
unsigned long tmp;
linux-2.6-utrace-regset-ia64.patch:
arch/ia64/ia32/sys_ia32.c | 472 +++++++++++++++++++++++++
arch/ia64/kernel/ptrace.c | 804 +++++++++++++++++++++++++++++++++++++++++++
include/asm-ia64/elf.h | 24 +
include/asm-ia64/tracehook.h | 7
4 files changed, 1305 insertions(+), 2 deletions(-)
--- NEW FILE linux-2.6-utrace-regset-ia64.patch ---
[PATCH 2a] utrace: ia64 regset support
This patch converts the machine-dependent ptrace code into utrace regset
support for ia64.
Signed-off-by: Roland McGrath <roland at redhat.com>
CC: Anil S Keshavamurthy <anil.s.keshavamurthy at intel.com>
Signed-off-by: Bibo Mao <bibo.mao at intel.com>
---
arch/ia64/ia32/sys_ia32.c | 472 +++++++++++++++++++++++++
arch/ia64/kernel/ptrace.c | 804 ++++++++++++++++++++++++++++++++++++++++++
include/asm-ia64/tracehook.h | 7
include/asm-ia64/elf.h | 24 +
4 files changed, 1305 insertions(+), 2 deletions(-)
--- linux-2.6/arch/ia64/ia32/sys_ia32.c
+++ linux-2.6/arch/ia64/ia32/sys_ia32.c
@@ -44,6 +44,7 @@
#include <linux/eventpoll.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/stat.h>
#include <linux/ipc.h>
#include <linux/capability.h>
@@ -1868,6 +1869,477 @@ sys32_ptrace (int request, pid_t pid, un
}
#endif
+#ifdef CONFIG_UTRACE
+typedef struct utrace_get {
+ void *kbuf;
+ void __user *ubuf;
+} utrace_get_t;
+
+typedef struct utrace_set {
+ const void *kbuf;
+ const void __user *ubuf;
+} utrace_set_t;
+
+typedef struct utrace_getset {
+ struct task_struct *target;
+ const struct utrace_regset *regset;
+ union {
+ utrace_get_t get;
+ utrace_set_t set;
+ } u;
+ unsigned int pos;
+ unsigned int count;
+ int ret;
+} utrace_getset_t;
+
+static void getfpreg(struct task_struct *task, int regno,int *val)
+{
+ switch (regno / sizeof(int)) {
+ case 0: *val = task->thread.fcr & 0xffff; break;
+ case 1: *val = task->thread.fsr & 0xffff; break;
+ case 2: *val = (task->thread.fsr>>16) & 0xffff; break;
+ case 3: *val = task->thread.fir; break;
+ case 4: *val = (task->thread.fir>>32) & 0xffff; break;
+ case 5: *val = task->thread.fdr; break;
+ case 6: *val = (task->thread.fdr >> 32) & 0xffff; break;
+ }
+}
+
+static void setfpreg(struct task_struct *task, int regno, int val)
+{
+ switch (regno / sizeof(int)) {
+ case 0:
+ task->thread.fcr = (task->thread.fcr & (~0x1f3f))
+ | (val & 0x1f3f);
+ break;
+ case 1:
+ task->thread.fsr = (task->thread.fsr & (~0xffff)) | val;
+ break;
+ case 2:
+ task->thread.fsr = (task->thread.fsr & (~0xffff0000))
+ | (val << 16);
+ break;
+ case 3:
+ task->thread.fir = (task->thread.fir & (~0xffffffff)) | val;
+ break;
+ case 5:
+ task->thread.fdr = (task->thread.fdr & (~0xffffffff)) | val;
+ break;
+ }
+}
+
+static void access_fpreg_ia32(int regno, void *reg,
+ struct pt_regs *pt, struct switch_stack *sw,
+ int tos, int write)
+{
+ void *f;
+
+ if ((regno += tos) >= 8)
+ regno -= 8;
+ if (regno <= 4)
+ f = &pt->f8 + regno;
+ else if (regno <= 7)
+ f = &sw->f12 + (regno - 4);
+ else {
+ printk(" regno must be less than 7 \n");
+ return;
+ }
+
+ if (write)
+ memcpy(f, reg, sizeof(struct _fpreg_ia32));
+ else
+ memcpy(reg, f, sizeof(struct _fpreg_ia32));
+}
+
+static void do_fpregs_get(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ struct task_struct *task = dst->target;
+ struct pt_regs *pt;
+ int start, end, tos;
+ char buf[80];
+
+ if (dst->count == 0 || unw_unwind_to_user(info) < 0)
+ return;
+ if (dst->pos < 7 * sizeof(int)) {
+ end = min((dst->pos + dst->count), (unsigned int)(7 * sizeof(int)));
+ for (start = dst->pos; start < end; start += sizeof(int))
+ getfpreg(task, start,(int *)( buf + start));
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, buf,
+ 0, 7 * sizeof(int));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+ if (dst->pos < sizeof(struct ia32_user_i387_struct)) {
+ pt = task_pt_regs(task);
+ tos = (task->thread.fsr >> 11) & 7;
+ end = min(dst->pos + dst->count,
+ (unsigned int)(sizeof(struct ia32_user_i387_struct)));
+ start = (dst->pos - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32);
+ end = (end - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32);
+ for (; start < end; start++)
+ access_fpreg_ia32(start, (struct _fpreg_ia32 *)buf + start,
+ pt, info->sw, tos, 0);
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ buf, 7 * sizeof(int),
+ sizeof(struct ia32_user_i387_struct));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+}
+
+static void do_fpregs_set(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ struct task_struct *task = dst->target;
+ struct pt_regs *pt;
+ char buf[80];
+ int end, start, tos;
+
+ if (dst->count == 0 || unw_unwind_to_user(info) < 0)
+ return;
+
+ if (dst->pos < 7 * sizeof(int)) {
+ start = dst->pos;
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, buf,
+ 0, 7 * sizeof(int));
+ if (dst->ret)
+ return;
+ for (; start < dst->pos; start += sizeof(int))
+ setfpreg(task, start, *((int*)(buf + start)));
+ if (dst->count == 0)
+ return;
+ }
+ if (dst->pos < sizeof(struct ia32_user_i387_struct)) {
+ start = (dst->pos - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf,
+ buf, 7 * sizeof(int),
+ sizeof(struct ia32_user_i387_struct));
+ if (dst->ret)
+ return;
+ pt = task_pt_regs(task);
+ tos = (task->thread.fsr >> 11) & 7;
+ end = (dst->pos - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32);
+ for (; start < end; start++)
+ access_fpreg_ia32(start, (struct _fpreg_ia32 *)buf + start,
+ pt, info->sw, tos, 0);
+ if (dst->count == 0)
+ return;
+ }
+}
+
+#define OFFSET(member) ((int)(offsetof(struct ia32_user_fxsr_struct, member)))
+static void getfpxreg(struct task_struct *task, int start, int end, char *buf)
+{
+ int min_val;
+
+ min_val = min(end, OFFSET(fop));
+ while (start < min_val) {
+ if (start == OFFSET(cwd))
+ *((short *)buf) = task->thread.fcr & 0xffff;
+ else if (start == OFFSET(swd))
+ *((short *)buf) = task->thread.fsr & 0xffff;
+ else if (start == OFFSET(twd))
+ *((short *)buf) = (task->thread.fsr>>16) & 0xffff;
+ buf += 2;
+ start += 2;
+ }
+ /* skip fop element */
+ if (start == OFFSET(fop)) {
+ start += 2;
+ buf += 2;
+ }
+ while (start < end) {
+ if (start == OFFSET(fip))
+ *((int *)buf) = task->thread.fir;
+ else if (start == OFFSET(fcs))
+ *((int *)buf) = (task->thread.fir>>32) & 0xffff;
+ else if (start == OFFSET(foo))
+ *((int *)buf) = task->thread.fdr;
+ else if (start == OFFSET(fos))
+ *((int *)buf) = (task->thread.fdr>>32) & 0xffff;
+ else if (start == OFFSET(mxcsr))
+ *((int *)buf) = ((task->thread.fcr>>32) & 0xff80)
+ | ((task->thread.fsr>>32) & 0x3f);
+ buf += 4;
+ start += 4;
+ }
+}
+
+static void setfpxreg(struct task_struct *task, int start, int end, char *buf)
+{
+ int min_val, num32;
+ short num;
+ unsigned long num64;
+
+ min_val = min(end, OFFSET(fop));
+ while (start < min_val) {
+ num = *((short *)buf);
+ if (start == OFFSET(cwd)) {
+ task->thread.fcr = (task->thread.fcr & (~0x1f3f))
+ | (num & 0x1f3f);
+ } else if (start == OFFSET(swd)) {
+ task->thread.fsr = (task->thread.fsr & (~0xffff)) | num;
+ } else if (start == OFFSET(twd)) {
+ task->thread.fsr = (task->thread.fsr & (~0xffff0000)) | num;
+ }
+ buf += 2;
+ start += 2;
+ }
+ /* skip fop element */
+ if (start == OFFSET(fop)) {
+ start += 2;
+ buf += 2;
+ }
+ while (start < end) {
+ num32 = *((int *)buf);
+ if (start == OFFSET(fip))
+ task->thread.fir = (task->thread.fir & (~0xffffffff))
+ | num32;
+ else if (start == OFFSET(foo))
+ task->thread.fdr = (task->thread.fdr & (~0xffffffff))
+ | num32;
+ else if (start == OFFSET(mxcsr)) {
+ num64 = num32 & 0xff10;
+ task->thread.fcr = (task->thread.fcr & (~0xff1000000000UL))
+ | (num64<<32);
+ num64 = num32 & 0x3f;
+ task->thread.fsr = (task->thread.fsr & (~0x3f00000000UL))
+ | (num64<<32);
+ }
+ buf += 4;
+ start += 4;
+ }
+}
+
+static void do_fpxregs_get(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ struct task_struct *task = dst->target;
+ struct pt_regs *pt;
+ char buf[128];
+ int start, end, tos;
+
+ if (dst->count == 0 || unw_unwind_to_user(info) < 0)
+ return;
+ if (dst->pos < OFFSET(st_space[0])) {
+ end = min(dst->pos + dst->count, (unsigned int)32);
+ getfpxreg(task, dst->pos, end, buf);
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, buf,
+ 0, OFFSET(st_space[0]));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+ if (dst->pos < OFFSET(xmm_space[0])) {
+ pt = task_pt_regs(task);
+ tos = (task->thread.fsr >> 11) & 7;
+ end = min(dst->pos + dst->count,
+ (unsigned int)OFFSET(xmm_space[0]));
+ start = (dst->pos - OFFSET(st_space[0])) / 16;
+ end = (end - OFFSET(st_space[0])) / 16;
+ for (; start < end; start++)
+ access_fpreg_ia32(start, buf + 16 * start, pt,
+ info->sw, tos, 0);
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ buf, OFFSET(st_space[0]), OFFSET(xmm_space[0]));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+ if (dst->pos < OFFSET(padding[0]))
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ &info->sw->f16, OFFSET(xmm_space[0]),
+ OFFSET(padding[0]));
+}
+
+static void do_fpxregs_set(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ struct task_struct *task = dst->target;
+ char buf[128];
+ int start, end;
+
+ if (dst->count == 0 || unw_unwind_to_user(info) < 0)
+ return;
+
+ if (dst->pos < OFFSET(st_space[0])) {
+ start = dst->pos;
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf,
+ buf, 0, OFFSET(st_space[0]));
+ if (dst->ret)
+ return;
+ setfpxreg(task, start, dst->pos, buf);
+ if (dst->count == 0)
+ return;
+ }
+ if (dst->pos < OFFSET(xmm_space[0])) {
+ struct pt_regs *pt;
+ int tos;
+ pt = task_pt_regs(task);
+ tos = (task->thread.fsr >> 11) & 7;
+ start = (dst->pos - OFFSET(st_space[0])) / 16;
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf,
+ buf, OFFSET(st_space[0]), OFFSET(xmm_space[0]));
+ if (dst->ret)
+ return;
+ end = (dst->pos - OFFSET(st_space[0])) / 16;
+ for (; start < end; start++)
+ access_fpreg_ia32(start, buf + 16 * start, pt, info->sw,
+ tos, 1);
+ if (dst->count == 0)
+ return;
+ }
+ if (dst->pos < OFFSET(padding[0]))
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf,
+ &info->sw->f16, OFFSET(xmm_space[0]),
+ OFFSET(padding[0]));
+}
+#undef OFFSET
+
+static int do_regset_call(void (*call)(struct unw_frame_info *, void *),
+ struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ utrace_getset_t info = { .target = target, .regset = regset,
+ .pos = pos, .count = count,
+ .u.set = { .kbuf = kbuf, .ubuf = ubuf },
+ .ret = 0 };
+
+ if (target == current)
+ unw_init_running(call, &info);
+ else {
+ struct unw_frame_info ufi;
+ memset(&ufi, 0, sizeof(ufi));
+ unw_init_from_blocked_task(&ufi, target);
+ (*call)(&ufi, &info);
+ }
+
+ return info.ret;
+}
+
+static int ia32_fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return do_regset_call(do_fpregs_get, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int ia32_fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return do_regset_call(do_fpregs_set, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int ia32_fpxregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return do_regset_call(do_fpxregs_get, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int ia32_fpxregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return do_regset_call(do_fpxregs_set, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int ia32_genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (kbuf) {
+ u32 *kp = kbuf;
+ while (count > 0) {
+ *kp++ = getreg(target, pos);
+ pos += 4;
+ count -= 4;
+ }
+ } else {
+ u32 __user *up = ubuf;
+ while (count > 0) {
+ if (__put_user(getreg(target, pos), up++))
+ return -EFAULT;
+ pos += 4;
+ count -= 4;
+ }
+ }
+ return 0;
+}
+
+static int ia32_genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+
+ if (kbuf) {
+ const u32 *kp = kbuf;
+ while (!ret && count > 0) {
+ putreg(target, pos, *kp++);
+ pos += 4;
+ count -= 4;
+ }
+ } else {
+ const u32 __user *up = ubuf;
+ u32 val;
+ while (!ret && count > 0) {
+ ret = __get_user(val, up++);
+ if (!ret)
+ putreg(target, pos, val);
+ pos += 4;
+ count -= 4;
+ }
+ }
+ return ret;
+}
+
+/*
+ * This should match arch/i386/kernel/ptrace.c:native_regsets.
+ * XXX ioperm? vm86?
+ */
+static const struct utrace_regset ia32_regsets[] = {
+ {
+ .n = sizeof(struct user_regs_struct32)/4,
+ .size = 4, .align = 4,
+ .get = ia32_genregs_get, .set = ia32_genregs_set
+ },
+ {
+ .n = sizeof(struct ia32_user_i387_struct) / 4,
+ .size = 4, .align = 4,
+ .get = ia32_fpregs_get, .set = ia32_fpregs_set
+ },
+ {
+ .n = sizeof(struct ia32_user_fxsr_struct) / 4,
+ .size = 4, .align = 4,
+ .get = ia32_fpxregs_get, .set = ia32_fpxregs_set
+ },
+};
+
+const struct utrace_regset_view utrace_ia32_view = {
+ .name = "i386", .e_machine = EM_386,
+ .regsets = ia32_regsets, .n = ARRAY_SIZE(ia32_regsets)
+};
+#endif
+
typedef struct {
unsigned int ss_sp;
unsigned int ss_flags;
--- linux-2.6/arch/ia64/kernel/ptrace.c
+++ linux-2.6/arch/ia64/kernel/ptrace.c
@@ -3,6 +3,9 @@
*
* Copyright (C) 1999-2005 Hewlett-Packard Co
* David Mosberger-Tang <davidm at hpl.hp.com>
+ * Copyright (C) 2006 Intel Co
+ * 2006-08-12 - IA64 Native Utrace implementation support added by
+ * Anil S Keshavamurthy <anil.s.keshavamurthy at intel.com>
*
* Derived from the x86 and Alpha versions.
*/
@@ -18,13 +21,16 @@
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/signal.h>
+#include <linux/module.h>
+#include <asm/tracehook.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/ptrace_offsets.h>
#include <asm/rse.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#include <asm/elf.h>
#include <asm/unwind.h>
#ifdef CONFIG_PERFMON
#include <asm/perfmon.h>
@@ -548,6 +554,7 @@ ia64_sync_user_rbs (struct task_struct *
return 0;
}
+#if 0 /* XXX */
static inline int
thread_matches (struct task_struct *thread, unsigned long addr)
{
@@ -620,6 +627,7 @@ find_thread_for_addr (struct task_struct
mmput(mm);
return child;
}
+#endif
/*
* Write f32-f127 back to task->thread.fph if it has been modified.
@@ -664,6 +672,7 @@ ia64_sync_fph (struct task_struct *task)
psr->dfh = 1;
}
+#if 0
static int
access_fr (struct unw_frame_info *info, int regnum, int hi,
unsigned long *data, int write_access)
@@ -682,6 +691,7 @@ access_fr (struct unw_frame_info *info,
*data = fpval.u.bits[hi];
return ret;
}
+#endif /* access_fr() */
/*
* Change the machine-state of CHILD such that it will return via the normal
@@ -782,6 +792,7 @@ access_nat_bits (struct task_struct *chi
return 0;
}
+#if 0
static int
access_uarea (struct task_struct *child, unsigned long addr,
unsigned long *data, int write_access)
@@ -1248,7 +1259,9 @@ ptrace_getregs (struct task_struct *chil
ret = retval ? -EIO : 0;
return ret;
}
+#endif /* ptrace_getregs() */
+#if 0
static long
ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
{
@@ -1394,6 +1407,7 @@ ptrace_setregs (struct task_struct *chil
ret = retval ? -EIO : 0;
return ret;
}
+#endif /* ptrace_setregs() */
/*
* Called by kernel/ptrace.c when detaching..
@@ -1411,6 +1425,7 @@ ptrace_disable (struct task_struct *chil
child_psr->tb = 0;
}
+#if 0 /* XXX */
asmlinkage long
sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
{
@@ -1598,6 +1613,7 @@ sys_ptrace (long request, pid_t pid, uns
unlock_kernel();
return ret;
}
+#endif
/* "asmlinkage" so the input arguments are preserved... */
@@ -1650,3 +1666,791 @@ syscall_trace_leave (long arg0, long arg
tracehook_report_syscall_step(®s);
}
}
+
+/* Utrace implementation starts here */
+
+typedef struct utrace_get {
+ void *kbuf;
+ void __user *ubuf;
+} utrace_get_t;
+
+typedef struct utrace_set {
+ const void *kbuf;
+ const void __user *ubuf;
+} utrace_set_t;
+
+typedef struct utrace_getset {
+ struct task_struct *target;
+ const struct utrace_regset *regset;
+ union {
+ utrace_get_t get;
+ utrace_set_t set;
+ } u;
+ unsigned int pos;
+ unsigned int count;
+ int ret;
+} utrace_getset_t;
+
+static int
+access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ struct pt_regs *pt;
+ unsigned long *ptr = NULL;
+ int ret;
+ char nat=0;
+
+ pt = task_pt_regs(target);
+ switch (addr) {
+ case ELF_GR_OFFSET(1):
+ ptr = &pt->r1;
+ break;
+ case ELF_GR_OFFSET(2):
+ case ELF_GR_OFFSET(3):
+ ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2));
+ break;
+ case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7):
+ if (write_access) {
+ /* read NaT bit first: */
+ unsigned long dummy;
+
+ ret = unw_get_gr(info, addr/8, &dummy, &nat);
+ if (ret < 0)
+ return ret;
+ }
+ return unw_access_gr(info, addr/8, data, &nat, write_access);
+ case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11):
+ ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8);
+ break;
+ case ELF_GR_OFFSET(12):
+ case ELF_GR_OFFSET(13):
+ ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12);
+ break;
+ case ELF_GR_OFFSET(14):
+ ptr = &pt->r14;
+ break;
+ case ELF_GR_OFFSET(15):
+ ptr = &pt->r15;
+ }
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+ return 0;
+}
+
+static int
+access_elf_breg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ struct pt_regs *pt;
+ unsigned long *ptr = NULL;
+
+ pt = task_pt_regs(target);
+ switch (addr) {
+ case ELF_BR_OFFSET(0):
+ ptr = &pt->b0;
+ break;
+ case ELF_BR_OFFSET(1) ... ELF_BR_OFFSET(5):
+ return unw_access_br(info, (addr - ELF_BR_OFFSET(0))/8,
+ data, write_access);
+ case ELF_BR_OFFSET(6):
+ ptr = &pt->b6;
+ break;
+ case ELF_BR_OFFSET(7):
+ ptr = &pt->b7;
+ }
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+ return 0;
+}
+
+static int
+access_elf_areg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ struct pt_regs *pt;
+ unsigned long cfm, urbs_end, rnat_addr;
+ unsigned long *ptr = NULL;
+
+ pt = task_pt_regs(target);
+ if (addr >= ELF_AR_RSC_OFFSET && addr <= ELF_AR_SSD_OFFSET) {
+ switch (addr) {
+ case ELF_AR_RSC_OFFSET:
+ /* force PL3 */
+ if (write_access)
+ pt->ar_rsc = *data | (3 << 2);
+ else
+ *data = pt->ar_rsc;
+ return 0;
+ case ELF_AR_BSP_OFFSET:
+ /*
+ * By convention, we use PT_AR_BSP to refer to
+ * the end of the user-level backing store.
+ * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof)
+ * to get the real value of ar.bsp at the time
+ * the kernel was entered.
+ *
+ * Furthermore, when changing the contents of
+ * PT_AR_BSP (or PT_CFM) we MUST copy any
+ * users-level stacked registers that are
+ * stored on the kernel stack back to
+ * user-space because otherwise, we might end
+ * up clobbering kernel stacked registers.
+ * Also, if this happens while the task is
+ * blocked in a system call, which convert the
+ * state such that the non-system-call exit
+ * path is used. This ensures that the proper
+ * state will be picked up when resuming
+ * execution. However, it *also* means that
+ * once we write PT_AR_BSP/PT_CFM, it won't be
+ * possible to modify the syscall arguments of
+ * the pending system call any longer. This
+ * shouldn't be an issue because modifying
+ * PT_AR_BSP/PT_CFM generally implies that
+ * we're either abandoning the pending system
+ * call or that we defer it's re-execution
+ * (e.g., due to GDB doing an inferior
+ * function call).
+ */
+ urbs_end = ia64_get_user_rbs_end(target, pt, &cfm);
+ if (write_access) {
+ if (*data != urbs_end) {
+ if (ia64_sync_user_rbs(target, info->sw,
+ pt->ar_bspstore,
+ urbs_end) < 0)
+ return -1;
+ if (in_syscall(pt))
+ convert_to_non_syscall(target,
+ pt,
+ cfm);
+ /*
+ * Simulate user-level write
+ * of ar.bsp:
+ */
+ pt->loadrs = 0;
+ pt->ar_bspstore = *data;
+ }
+ } else
+ *data = urbs_end;
+ return 0;
+ case ELF_AR_BSPSTORE_OFFSET: // ar_bsp_store
+ ptr = &pt->ar_bspstore;
+ break;
+ case ELF_AR_RNAT_OFFSET: // ar_rnat
+ urbs_end = ia64_get_user_rbs_end(target, pt, NULL);
+ rnat_addr = (long) ia64_rse_rnat_addr((long *)
+ urbs_end);
+ if (write_access)
+ return ia64_poke(target, info->sw, urbs_end,
+ rnat_addr, *data);
+ else
+ return ia64_peek(target, info->sw, urbs_end,
+ rnat_addr, data);
+ case ELF_AR_CCV_OFFSET: // ar_ccv
+ ptr = &pt->ar_ccv;
+ break;
+ case ELF_AR_UNAT_OFFSET: // ar_unat
+ ptr = &pt->ar_unat;
+ break;
+ case ELF_AR_FPSR_OFFSET: // ar_fpsr
+ ptr = &pt->ar_fpsr;
+ break;
+ case ELF_AR_PFS_OFFSET: // ar_pfs
+ ptr = &pt->ar_pfs;
+ break;
+ case ELF_AR_LC_OFFSET: // ar_lc
+ return unw_access_ar(info, UNW_AR_LC, data,
+ write_access);
+ case ELF_AR_EC_OFFSET: // ar_ec
+ return unw_access_ar(info, UNW_AR_EC, data,
+ write_access);
+ case ELF_AR_CSD_OFFSET: // ar_csd
+ ptr = &pt->ar_csd;
+ break;
+ case ELF_AR_SSD_OFFSET: // ar_ssd
+ ptr = &pt->ar_ssd;
+ }
+ } else if (addr >= ELF_CR_IIP_OFFSET && addr <= ELF_CR_IPSR_OFFSET) {
+ switch (addr) {
+ case ELF_CR_IIP_OFFSET:
+ ptr = &pt->cr_iip;
+ break;
+ case ELF_CFM_OFFSET:
+ urbs_end = ia64_get_user_rbs_end(target, pt, &cfm);
+ if (write_access) {
+ if (((cfm ^ *data) & PFM_MASK) != 0) {
+ if (ia64_sync_user_rbs(target, info->sw,
+ pt->ar_bspstore,
+ urbs_end) < 0)
+ return -1;
+ if (in_syscall(pt))
+ convert_to_non_syscall(target,
+ pt,
+ cfm);
+ pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK)
+ | (*data & PFM_MASK));
+ }
+ } else
+ *data = cfm;
+ return 0;
+ case ELF_CR_IPSR_OFFSET:
+ if (write_access)
+ pt->cr_ipsr = ((*data & IPSR_MASK)
+ | (pt->cr_ipsr & ~IPSR_MASK));
+ else
+ *data = (pt->cr_ipsr & IPSR_MASK);
+ return 0;
+ }
+ } else if (addr == ELF_NAT_OFFSET)
+ return access_nat_bits(target, pt, info,
+ data, write_access);
+ else if (addr == ELF_PR_OFFSET)
+ ptr = &pt->pr;
+ else
+ return -1;
+
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+
+ return 0;
+}
+
+static int
+access_elf_reg(struct task_struct *target, struct unw_frame_info *info,
+ unsigned long addr, unsigned long *data, int write_access)
+{
+ if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15))
+ return access_elf_gpreg(target, info, addr, data, write_access);
+ else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7))
+ return access_elf_breg(target, info, addr, data, write_access);
+ else
+ return access_elf_areg(target, info, addr, data, write_access);
+}
+
+void do_gpregs_get(struct unw_frame_info *info, void *arg)
+{
+ struct pt_regs *pt;
+ utrace_getset_t *dst = arg;
+ elf_greg_t tmp[16];
+ unsigned int i, index, min_copy;
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+
+ /*
+ * coredump format:
+ * r0-r31
+ * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT)
+ * predicate registers (p0-p63)
+ * b0-b7
+ * ip cfm user-mask
+ * ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec
+ */
+
+
+ /* Skip r0 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) {
+ dst->ret = utrace_regset_copyout_zero(&dst->pos, &dst->count,
+ &dst->u.get.kbuf,
+ &dst->u.get.ubuf,
+ 0, ELF_GR_OFFSET(1));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* gr1 - gr15 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) {
+ index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t);
+ min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ?
+ (dst->pos + dst->count) : ELF_GR_OFFSET(16);
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 0) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_GR_OFFSET(1), ELF_GR_OFFSET(16));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* r16-r31 */
+ if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) {
+ pt = task_pt_regs(dst->target);
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16,
+ ELF_GR_OFFSET(16), ELF_NAT_OFFSET);
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* nat, pr, b0 - b7 */
+ if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) {
+ index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t);
+ min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ?
+ (dst->pos + dst->count) : ELF_CR_IIP_OFFSET;
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 0) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET);
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd
+ */
+ if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) {
+ index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
+ min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ?
+ (dst->pos + dst->count) : ELF_AR_END_OFFSET;
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 0) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET);
+ }
+}
+
+void do_gpregs_set(struct unw_frame_info *info, void *arg)
+{
+ struct pt_regs *pt;
+ utrace_getset_t *dst = arg;
+ elf_greg_t tmp[16];
+ unsigned int i, index;
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+
+ /* Skip r0 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) {
+ dst->ret = utrace_regset_copyin_ignore(&dst->pos, &dst->count,
+ &dst->u.set.kbuf,
+ &dst->u.set.ubuf,
+ 0, ELF_GR_OFFSET(1));
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* gr1-gr15 */
+ if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) {
+ i = dst->pos;
+ index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_GR_OFFSET(1), ELF_GR_OFFSET(16));
+ if (dst->ret)
+ return;
+ for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 1) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ if (dst->count == 0)
+ return;
+ }
+
+ /* gr16-gr31 */
+ if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) {
+ pt = task_pt_regs(dst->target);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16,
+ ELF_GR_OFFSET(16), ELF_NAT_OFFSET);
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* nat, pr, b0 - b7 */
+ if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) {
+ i = dst->pos;
+ index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET);
+ if (dst->ret)
+ return;
+ for (; i < dst->pos; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 1) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ if (dst->count == 0)
+ return;
+ }
+
+ /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd
+ */
+ if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) {
+ i = dst->pos;
+ index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET);
+ if (dst->ret)
+ return;
+ for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++)
+ if (access_elf_reg(dst->target, info, i,
+ &tmp[index], 1) < 0) {
+ dst->ret = -EIO;
+ return;
+ }
+ }
+}
+
+#define ELF_FP_OFFSET(i) (i * sizeof(elf_fpreg_t))
+
+void do_fpregs_get(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ struct task_struct *task = dst->target;
+ elf_fpreg_t tmp[30];
+ int index, min_copy, i;
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+
+ /* Skip pos 0 and 1 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) {
+ dst->ret = utrace_regset_copyout_zero(&dst->pos, &dst->count,
+ &dst->u.get.kbuf,
+ &dst->u.get.ubuf,
+ 0, ELF_FP_OFFSET(2));
+ if (dst->count == 0 || dst->ret)
+ return;
+ }
+
+ /* fr2-fr31 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) {
+ index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t);
+ min_copy = min(((unsigned int)ELF_FP_OFFSET(32)),
+ dst->pos + dst->count);
+ for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), index++)
+ if (unw_get_fr(info, i / sizeof(elf_fpreg_t),
+ &tmp[index])) {
+ dst->ret = -EIO;
+ return;
+ }
+ dst->ret = utrace_regset_copyout(&dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
+ ELF_FP_OFFSET(2), ELF_FP_OFFSET(32));
+ if (dst->count == 0 || dst->ret)
+ return;
+ }
+
+ /* fph */
+ if (dst->count > 0) {
+ ia64_flush_fph(dst->target);
+ if (task->thread.flags & IA64_THREAD_FPH_VALID)
+ dst->ret = utrace_regset_copyout(
+ &dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ &dst->target->thread.fph,
+ ELF_FP_OFFSET(32), -1);
+ else
+ /* Zero fill instead. */
+ dst->ret = utrace_regset_copyout_zero(
+ &dst->pos, &dst->count,
+ &dst->u.get.kbuf, &dst->u.get.ubuf,
+ ELF_FP_OFFSET(32), -1);
+ }
+}
+
+void do_fpregs_set(struct unw_frame_info *info, void *arg)
+{
+ utrace_getset_t *dst = arg;
+ elf_fpreg_t fpreg, tmp[30];
+ int index, start, end;
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+
+ /* Skip pos 0 and 1 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) {
+ dst->ret = utrace_regset_copyin_ignore(&dst->pos, &dst->count,
+ &dst->u.set.kbuf,
+ &dst->u.set.ubuf,
+ 0, ELF_FP_OFFSET(2));
+ if (dst->count == 0 || dst->ret)
+ return;
+ }
+
+ /* fr2-fr31 */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) {
+ start = dst->pos;
+ end = min(((unsigned int)ELF_FP_OFFSET(32)),
+ dst->pos + dst->count);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
+ ELF_FP_OFFSET(2), ELF_FP_OFFSET(32));
+ if (dst->ret)
+ return;
+
+ if (start & 0xF) { // only write high part
+ if (unw_get_fr(info, start / sizeof(elf_fpreg_t),
+ &fpreg)) {
+ dst->ret = -EIO;
+ return;
+ }
+ tmp[start / sizeof(elf_fpreg_t) - 2].u.bits[0]
+ = fpreg.u.bits[0];
+ start &= ~0xFUL;
+ }
+ if (end & 0xF) { // only write low part
+ if (unw_get_fr(info, end / sizeof(elf_fpreg_t), &fpreg)) {
+ dst->ret = -EIO;
+ return;
+ }
+ tmp[end / sizeof(elf_fpreg_t) -2].u.bits[1]
+ = fpreg.u.bits[1];
+ end = (end + 0xF) & ~0xFUL;
+ }
+
+ for ( ; start < end ; start += sizeof(elf_fpreg_t)) {
+ index = start / sizeof(elf_fpreg_t);
+ if (unw_set_fr(info, index, tmp[index - 2])){
+ dst->ret = -EIO;
+ return;
+ }
+ }
+ if (dst->ret || dst->count == 0)
+ return;
+ }
+
+ /* fph */
+ if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(128)) {
+ ia64_sync_fph(dst->target);
+ dst->ret = utrace_regset_copyin(&dst->pos, &dst->count,
+ &dst->u.set.kbuf,
+ &dst->u.set.ubuf,
+ &dst->target->thread.fph,
+ ELF_FP_OFFSET(32), -1);
+ }
+}
+
+static int
+do_regset_call(void (*call)(struct unw_frame_info *, void *),
+ struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ utrace_getset_t info = { .target = target, .regset = regset,
+ .pos = pos, .count = count,
+ .u.set = { .kbuf = kbuf, .ubuf = ubuf },
+ .ret = 0 };
+
+ if (target == current)
+ unw_init_running(call, &info);
+ else {
+ struct unw_frame_info ufi;
+ memset(&ufi, 0, sizeof(ufi));
+ unw_init_from_blocked_task(&ufi, target);
+ (*call)(&ufi, &info);
+ }
+
+ return info.ret;
+}
+
+static int
+gpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return do_regset_call(do_gpregs_get, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int gpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return do_regset_call(do_gpregs_set, target, regset, pos, count, kbuf, ubuf);
+}
+
+static void do_gpregs_writeback(struct unw_frame_info *info, void *arg)
+{
+ struct pt_regs *pt;
+ utrace_getset_t *dst = arg;
+ unsigned long urbs_end;
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+ pt = task_pt_regs(dst->target);
+ urbs_end = ia64_get_user_rbs_end(dst->target, pt, NULL);
+ dst->ret = ia64_sync_user_rbs(dst->target, info->sw, pt->ar_bspstore, urbs_end);
+}
+/*
+ * This is called to write back the register backing store.
+ * ptrace does this before it stops, so that a tracer reading the user
+ * memory after the thread stops will get the current register data.
+ */
+static int
+gpregs_writeback(struct task_struct *target,
+ const struct utrace_regset *regset,
+ int now)
+{
+ return do_regset_call(do_gpregs_writeback, target, regset, 0, 0, NULL, NULL);
+}
+
+static int
+fpregs_active(struct task_struct *target, const struct utrace_regset *regset)
+{
+ return (target->thread.flags & IA64_THREAD_FPH_VALID) ? 128 : 32;
+}
+
+static int fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return do_regset_call(do_fpregs_get, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return do_regset_call(do_fpregs_set, target, regset, pos, count, kbuf, ubuf);
+}
+
+static int dbregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+#ifdef CONFIG_PERFMON
+ /*
+ * Check if debug registers are used by perfmon. This
+ * test must be done once we know that we can do the
+ * operation, i.e. the arguments are all valid, but
+ * before we start modifying the state.
+ *
+ * Perfmon needs to keep a count of how many processes
+ * are trying to modify the debug registers for system
+ * wide monitoring sessions.
+ *
+ * We also include read access here, because they may
+ * cause the PMU-installed debug register state
+ * (dbr[], ibr[]) to be reset. The two arrays are also
+ * used by perfmon, but we do not use
+ * IA64_THREAD_DBG_VALID. The registers are restored
+ * by the PMU context switch code.
+ */
+ if (pfm_use_debug_registers(target))
+ return -EIO;
+#endif
+
+ if (!(target->thread.flags & IA64_THREAD_DBG_VALID))
+ ret = utrace_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ 0, -1);
+ else {
+ preempt_disable();
+ if (target == current)
+ ia64_load_debug_regs(&target->thread.dbr[0]);
+ preempt_enable_no_resched();
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dbr, 0, -1);
+ }
+
+ return ret;
+}
+
+static int dbregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int i, ret;
+
+#ifdef CONFIG_PERFMON
+ if (pfm_use_debug_registers(target))
+ return -EIO;
+#endif
+
+ ret = 0;
+ if (!(target->thread.flags & IA64_THREAD_DBG_VALID)){
+ target->thread.flags |= IA64_THREAD_DBG_VALID;
+ memset(target->thread.dbr, 0, 2 * sizeof(target->thread.dbr));
+ } else if (target == current){
+ preempt_disable();
+ ia64_save_debug_regs(&target->thread.dbr[0]);
+ preempt_enable_no_resched();
+ }
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dbr, 0, -1);
+
+ for (i = 1; i < IA64_NUM_DBG_REGS; i += 2) {
+ target->thread.dbr[i] &= ~(7UL << 56);
+ target->thread.ibr[i] &= ~(7UL << 56);
+ }
+
+ if (ret)
+ return ret;
+
+ if (target == current){
+ preempt_disable();
+ ia64_load_debug_regs(&target->thread.dbr[0]);
+ preempt_enable_no_resched();
+ }
+ return 0;
+}
+
+static const struct utrace_regset native_regsets[] = {
+ {
+ .n = ELF_NGREG,
+ .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t),
+ .get = gpregs_get, .set = gpregs_set,
+ .writeback = gpregs_writeback
+ },
+ {
+ .n = ELF_NFPREG,
+ .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t),
+ .get = fpregs_get, .set = fpregs_set, .active = fpregs_active
+ },
+ {
+ .n = 2 * IA64_NUM_DBG_REGS, .size = sizeof(long),
+ .align = sizeof(long),
+ .get = dbregs_get, .set = dbregs_set
+ }
+};
+
+static const struct utrace_regset_view utrace_ia64_native = {
+ .name = "ia64",
+ .e_machine = EM_IA_64,
+ .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
+};
+
+const struct utrace_regset_view *utrace_native_view(struct task_struct *tsk)
+{
+#ifdef CONFIG_IA32_SUPPORT
+ extern const struct utrace_regset_view utrace_ia32_view;
+ if (IS_IA32_PROCESS(task_pt_regs(tsk)))
+ return &utrace_ia32_view;
+#endif
+ return &utrace_ia64_native;
+}
--- linux-2.6/include/asm-ia64/tracehook.h
+++ linux-2.6/include/asm-ia64/tracehook.h
@@ -67,7 +67,10 @@ static inline int tracehook_single_step_
static inline void tracehook_abort_syscall(struct pt_regs *regs)
{
- regs->r15 = -1L;
+ if (IS_IA32_PROCESS(regs))
+ regs->r1 = -1UL;
+ else
+ regs->r15 = -1UL;
}
-#endif
+#endif /* asm/tracehook.h */
--- linux-2.6/include/asm-ia64/elf.h
+++ linux-2.6/include/asm-ia64/elf.h
@@ -154,6 +154,30 @@ extern void ia64_init_addr_space (void);
#define ELF_NGREG 128 /* we really need just 72 but let's leave some headroom... */
#define ELF_NFPREG 128 /* f0 and f1 could be omitted, but so what... */
+/* elf_gregset_t register offsets */
+#define ELF_GR_0_OFFSET 0
+#define ELF_NAT_OFFSET (32 * sizeof(elf_greg_t))
+#define ELF_PR_OFFSET (33 * sizeof(elf_greg_t))
+#define ELF_BR_0_OFFSET (34 * sizeof(elf_greg_t))
+#define ELF_CR_IIP_OFFSET (42 * sizeof(elf_greg_t))
+#define ELF_CFM_OFFSET (43 * sizeof(elf_greg_t))
+#define ELF_CR_IPSR_OFFSET (44 * sizeof(elf_greg_t))
+#define ELF_GR_OFFSET(i) (ELF_GR_0_OFFSET + i * sizeof(elf_greg_t))
+#define ELF_BR_OFFSET(i) (ELF_BR_0_OFFSET + i * sizeof(elf_greg_t))
+#define ELF_AR_RSC_OFFSET (45 * sizeof(elf_greg_t))
+#define ELF_AR_BSP_OFFSET (46 * sizeof(elf_greg_t))
+#define ELF_AR_BSPSTORE_OFFSET (47 * sizeof(elf_greg_t))
+#define ELF_AR_RNAT_OFFSET (48 * sizeof(elf_greg_t))
+#define ELF_AR_CCV_OFFSET (49 * sizeof(elf_greg_t))
+#define ELF_AR_UNAT_OFFSET (50 * sizeof(elf_greg_t))
+#define ELF_AR_FPSR_OFFSET (51 * sizeof(elf_greg_t))
+#define ELF_AR_PFS_OFFSET (52 * sizeof(elf_greg_t))
+#define ELF_AR_LC_OFFSET (53 * sizeof(elf_greg_t))
+#define ELF_AR_EC_OFFSET (54 * sizeof(elf_greg_t))
+#define ELF_AR_CSD_OFFSET (55 * sizeof(elf_greg_t))
+#define ELF_AR_SSD_OFFSET (56 * sizeof(elf_greg_t))
+#define ELF_AR_END_OFFSET (57 * sizeof(elf_greg_t))
+
typedef unsigned long elf_fpxregset_t;
typedef unsigned long elf_greg_t;
linux-2.6-utrace-regset-s390.patch:
Makefile | 2
ptrace.c | 952 +++++++++++++++++++++++++--------------------------------------
2 files changed, 390 insertions(+), 564 deletions(-)
--- NEW FILE linux-2.6-utrace-regset-s390.patch ---
[PATCH 2c] utrace: s390 regset support
This patch converts the machine-dependent ptrace code into utrace regset
support for s390.
Signed-off-by: Roland McGrath <roland at redhat.com>
CC: David Wilder <dwilder at us.ibm.com>
---
arch/s390/kernel/Makefile | 2
arch/s390/kernel/ptrace.c | 952 ++++++++++++++++++---------------------------
2 files changed, 390 insertions(+), 564 deletions(-)
--- linux-2.6/arch/s390/kernel/Makefile
+++ linux-2.6/arch/s390/kernel/Makefile
@@ -35,3 +35,5 @@ obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS
# This is just to get the dependencies...
#
binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c
+
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
--- linux-2.6/arch/s390/kernel/ptrace.c
+++ linux-2.6/arch/s390/kernel/ptrace.c
@@ -30,6 +30,7 @@
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/tracehook.h>
+#include <linux/module.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
@@ -42,6 +43,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
+#include <asm/elf.h>
#ifdef CONFIG_COMPAT
#include "compat_ptrace.h"
@@ -116,640 +118,462 @@ tracehook_single_step_enabled(struct tas
return task->thread.per_info.single_step;
}
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
- */
-void
-ptrace_disable(struct task_struct *child)
-{
- /* make sure the single step bit is not set. */
- tracehook_disable_single_step(child);
-}
-
-#ifndef CONFIG_64BIT
-# define __ADDR_MASK 3
-#else
-# define __ADDR_MASK 7
-#endif
-/*
- * Read the word at offset addr from the user area of a process. The
- * trouble here is that the information is littered over different
- * locations. The process registers are found on the kernel stack,
- * the floating point stuff and the trace settings are stored in
- * the task structure. In addition the different structures in
- * struct user contain pad bytes that should be read as zeroes.
- * Lovely...
- */
static int
-peek_user(struct task_struct *child, addr_t addr, addr_t data)
+genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- struct user *dummy = NULL;
- addr_t offset, tmp, mask;
+ struct pt_regs *regs = task_pt_regs(target);
+ unsigned long pswmask;
+ int ret;
- /*
- * Stupid gdb peeks/pokes the access registers in 64 bit with
- * an alignment of 4. Programmers from hell...
- */
- mask = __ADDR_MASK;
-#ifdef CONFIG_64BIT
- if (addr >= (addr_t) &dummy->regs.acrs &&
- addr < (addr_t) &dummy->regs.orig_gpr2)
- mask = 3;
-#endif
- if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
- return -EIO;
+ /* Remove per bit from user psw. */
+ pswmask = regs->psw.mask & ~PSW_MASK_PER;
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK, PT_PSWADDR);
+
+ /* The rest of the PSW and the GPRs are directly on the stack. */
+ if (ret == 0)
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ ®s->psw.addr, PT_PSWADDR,
+ PT_ACR0);
+
+ /* The ACRs are kept in the thread_struct. */
+ if (ret == 0 && count > 0 && pos < PT_ORIGGPR2) {
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0, PT_ORIGGPR2);
+ }
- if (addr < (addr_t) &dummy->regs.acrs) {
- /*
- * psw and gprs are stored on the stack
- */
- tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
- if (addr == (addr_t) &dummy->regs.psw.mask)
- /* Remove per bit from user psw. */
- tmp &= ~PSW_MASK_PER;
+ /* Finally, the ORIG_GPR2 value. */
+ if (ret == 0)
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ ®s->orig_gpr2, PT_ORIGGPR2, -1);
- } else if (addr < (addr_t) &dummy->regs.orig_gpr2) {
- /*
- * access registers are stored in the thread structure
- */
- offset = addr - (addr_t) &dummy->regs.acrs;
-#ifdef CONFIG_64BIT
- /*
- * Very special case: old & broken 64 bit gdb reading
- * from acrs[15]. Result is a 64 bit value. Read the
- * 32 bit acrs[15] value and shift it by 32. Sick...
- */
- if (addr == (addr_t) &dummy->regs.acrs[15])
- tmp = ((unsigned long) child->thread.acrs[15]) << 32;
- else
-#endif
- tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset);
+ return ret;
+}
- } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
- /*
- * orig_gpr2 is stored on the kernel stack
- */
- tmp = (addr_t) task_pt_regs(child)->orig_gpr2;
+static int
+genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ int ret = 0;
+
+ /* Check for an invalid PSW mask. */
+ if (count > 0 && pos == PT_PSWMASK) {
+ unsigned long pswmask = regs->psw.mask;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK, PT_PSWADDR);
+ if (pswmask != PSW_MASK_MERGE(psw_user_bits, pswmask)
+#ifdef CONFIG_COMPAT
+ && pswmask != PSW_MASK_MERGE(psw_user32_bits, pswmask)
+#endif
+ )
+ /* Invalid psw mask. */
+ return -EINVAL;
+ regs->psw.mask = pswmask;
+ FixPerRegisters(target);
+ }
- } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
- /*
- * floating point regs. are stored in the thread structure
- */
- offset = addr - (addr_t) &dummy->regs.fp_regs;
- tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset);
- if (addr == (addr_t) &dummy->regs.fp_regs.fpc)
- tmp &= (unsigned long) FPC_VALID_MASK
- << (BITS_PER_LONG - 32);
+ /* The rest of the PSW and the GPRs are directly on the stack. */
+ if (ret == 0) {
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ ®s->psw.addr, PT_PSWADDR,
+ PT_ACR0);
+#ifndef CONFIG_64BIT
+ /* I'd like to reject addresses without the
+ high order bit but older gdb's rely on it */
+ regs->psw.addr |= PSW_ADDR_AMODE;
+#endif
+ }
- } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
- /*
- * per_info is found in the thread structure
- */
- offset = addr - (addr_t) &dummy->regs.per_info;
- tmp = *(addr_t *)((addr_t) &child->thread.per_info + offset);
+ /* The ACRs are kept in the thread_struct. */
+ if (ret == 0 && count > 0 && pos < PT_ORIGGPR2) {
+ if (target == current
+ && (pos != PT_ACR0 || count < sizeof(target->thread.acrs)))
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0, PT_ORIGGPR2);
+ if (ret == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+ }
- } else
- tmp = 0;
+ /* Finally, the ORIG_GPR2 value. */
+ if (ret == 0)
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ ®s->orig_gpr2, PT_ORIGGPR2, -1);
- return put_user(tmp, (addr_t __user *) data);
+ return ret;
}
-/*
- * Write a word to the user area of a process at location addr. This
- * operation does have an additional problem compared to peek_user.
- * Stores to the program status word and on the floating point
- * control register needs to get checked for validity.
- */
static int
-poke_user(struct task_struct *child, addr_t addr, addr_t data)
+fpregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- struct user *dummy = NULL;
- addr_t offset, mask;
+ if (target == current)
+ save_fp_regs(&target->thread.fp_regs);
- /*
- * Stupid gdb peeks/pokes the access registers in 64 bit with
- * an alignment of 4. Programmers from hell indeed...
- */
- mask = __ADDR_MASK;
-#ifdef CONFIG_64BIT
- if (addr >= (addr_t) &dummy->regs.acrs &&
- addr < (addr_t) &dummy->regs.orig_gpr2)
- mask = 3;
-#endif
- if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
- return -EIO;
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fp_regs, 0, -1);
+}
- if (addr < (addr_t) &dummy->regs.acrs) {
- /*
- * psw and gprs are stored on the stack
- */
- if (addr == (addr_t) &dummy->regs.psw.mask &&
-#ifdef CONFIG_COMPAT
- data != PSW_MASK_MERGE(psw_user32_bits, data) &&
-#endif
- data != PSW_MASK_MERGE(psw_user_bits, data))
- /* Invalid psw mask. */
+static int
+fpregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+
+ if (target == current && (pos != 0 || count != sizeof(s390_fp_regs)))
+ save_fp_regs(&target->thread.fp_regs);
+
+ /* If setting FPC, must validate it first. */
+ if (count > 0 && pos == 0) {
+ unsigned long fpc;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fpc, 0, sizeof(fpc));
+ if (ret)
+ return ret;
+
+ if ((fpc & ~((unsigned long) FPC_VALID_MASK
+ << (BITS_PER_LONG - 32))) != 0)
return -EINVAL;
-#ifndef CONFIG_64BIT
- if (addr == (addr_t) &dummy->regs.psw.addr)
- /* I'd like to reject addresses without the
- high order bit but older gdb's rely on it */
- data |= PSW_ADDR_AMODE;
-#endif
- *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
- } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
- /*
- * access registers are stored in the thread structure
- */
- offset = addr - (addr_t) &dummy->regs.acrs;
-#ifdef CONFIG_64BIT
- /*
- * Very special case: old & broken 64 bit gdb writing
- * to acrs[15] with a 64 bit value. Ignore the lower
- * half of the value and write the upper 32 bit to
- * acrs[15]. Sick...
- */
- if (addr == (addr_t) &dummy->regs.acrs[15])
- child->thread.acrs[15] = (unsigned int) (data >> 32);
- else
-#endif
- *(addr_t *)((addr_t) &child->thread.acrs + offset) = data;
+ memcpy(&target->thread.fp_regs, &fpc, sizeof(fpc));
+ }
- } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
- /*
- * orig_gpr2 is stored on the kernel stack
- */
- task_pt_regs(child)->orig_gpr2 = data;
+ if (ret == 0)
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fp_regs, 0, -1);
- } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
- /*
- * floating point regs. are stored in the thread structure
- */
- if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
- (data & ~((unsigned long) FPC_VALID_MASK
- << (BITS_PER_LONG - 32))) != 0)
- return -EINVAL;
- offset = addr - (addr_t) &dummy->regs.fp_regs;
- *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
+ if (ret == 0 && target == current)
+ restore_fp_regs(&target->thread.fp_regs);
- } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
- /*
- * per_info is found in the thread structure
- */
- offset = addr - (addr_t) &dummy->regs.per_info;
- *(addr_t *)((addr_t) &child->thread.per_info + offset) = data;
-
- }
+ return ret;
+}
- FixPerRegisters(child);
- return 0;
+static int
+per_info_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.per_info, 0, -1);
}
static int
-do_ptrace_normal(struct task_struct *child, long request, long addr, long data)
+per_info_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- unsigned long tmp;
- ptrace_area parea;
- int copied, ret;
-
- switch (request) {
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- /* Remove high order bit from address (only for 31 bit). */
- addr &= PSW_ADDR_INSN;
- /* read word at location addr. */
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- if (copied != sizeof(tmp))
- return -EIO;
- return put_user(tmp, (unsigned long __force __user *) data);
-
- case PTRACE_PEEKUSR:
- /* read the word at location addr in the USER area. */
- return peek_user(child, addr, data);
-
- case PTRACE_POKETEXT:
- case PTRACE_POKEDATA:
- /* Remove high order bit from address (only for 31 bit). */
- addr &= PSW_ADDR_INSN;
- /* write the word at location addr. */
- copied = access_process_vm(child, addr, &data, sizeof(data),1);
- if (copied != sizeof(data))
- return -EIO;
- return 0;
-
- case PTRACE_POKEUSR:
- /* write the word at location addr in the USER area */
- return poke_user(child, addr, data);
-
- case PTRACE_PEEKUSR_AREA:
- case PTRACE_POKEUSR_AREA:
- if (copy_from_user(&parea, (void __force __user *) addr,
- sizeof(parea)))
- return -EFAULT;
- addr = parea.kernel_addr;
- data = parea.process_addr;
- copied = 0;
- while (copied < parea.len) {
- if (request == PTRACE_PEEKUSR_AREA)
- ret = peek_user(child, addr, data);
- else {
- addr_t utmp;
- if (get_user(utmp,
- (addr_t __force __user *) data))
- return -EFAULT;
- ret = poke_user(child, addr, utmp);
- }
- if (ret)
- return ret;
- addr += sizeof(unsigned long);
- data += sizeof(unsigned long);
- copied += sizeof(unsigned long);
- }
- return 0;
- }
- return ptrace_request(child, request, addr, data);
+ int ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.per_info, 0, -1);
+
+ FixPerRegisters(target);
+
+ return ret;
}
-#ifdef CONFIG_COMPAT
-/*
- * Now the fun part starts... a 31 bit program running in the
- * 31 bit emulation tracing another program. PTRACE_PEEKTEXT,
- * PTRACE_PEEKDATA, PTRACE_POKETEXT and PTRACE_POKEDATA are easy
- * to handle, the difference to the 64 bit versions of the requests
- * is that the access is done in multiples of 4 byte instead of
- * 8 bytes (sizeof(unsigned long) on 31/64 bit).
- * The ugly part are PTRACE_PEEKUSR, PTRACE_PEEKUSR_AREA,
- * PTRACE_POKEUSR and PTRACE_POKEUSR_AREA. If the traced program
- * is a 31 bit program too, the content of struct user can be
- * emulated. A 31 bit program peeking into the struct user of
- * a 64 bit program is a no-no.
- */
/*
- * Same as peek_user but for a 31 bit program.
+ * These are our native regset flavors.
*/
-static int
-peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
-{
- struct user32 *dummy32 = NULL;
- per_struct32 *dummy_per32 = NULL;
- addr_t offset;
- __u32 tmp;
-
- if (!test_thread_flag(TIF_31BIT) ||
- (addr & 3) || addr > sizeof(struct user) - 3)
- return -EIO;
+static const struct utrace_regset native_regsets[] = {
+ {
+ .size = sizeof(long), .align = sizeof(long),
+ .n = sizeof(s390_regs) / sizeof(long),
+ .get = genregs_get, .set = genregs_set
+ },
+ {
+ .size = sizeof(long), .align = sizeof(long),
+ .n = sizeof(s390_fp_regs) / sizeof(long),
+ .get = fpregs_get, .set = fpregs_set
+ },
+ {
+ .size = sizeof(long), .align = sizeof(long),
+ .n = sizeof(per_struct) / sizeof(long),
+ .get = per_info_get, .set = per_info_set
+ },
+};
+
+static const struct utrace_regset_view utrace_s390_native_view = {
+ .name = UTS_MACHINE, .e_machine = ELF_ARCH,
+ .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
+};
- if (addr < (addr_t) &dummy32->regs.acrs) {
- /*
- * psw and gprs are stored on the stack
- */
- if (addr == (addr_t) &dummy32->regs.psw.mask) {
- /* Fake a 31 bit psw mask. */
- tmp = (__u32)(task_pt_regs(child)->psw.mask >> 32);
- tmp = PSW32_MASK_MERGE(psw32_user_bits, tmp);
- } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
- /* Fake a 31 bit psw address. */
- tmp = (__u32) task_pt_regs(child)->psw.addr |
- PSW32_ADDR_AMODE31;
- } else {
- /* gpr 0-15 */
- tmp = *(__u32 *)((addr_t) &task_pt_regs(child)->psw +
- addr*2 + 4);
- }
- } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) {
- /*
- * access registers are stored in the thread structure
- */
- offset = addr - (addr_t) &dummy32->regs.acrs;
- tmp = *(__u32*)((addr_t) &child->thread.acrs + offset);
- } else if (addr == (addr_t) (&dummy32->regs.orig_gpr2)) {
- /*
- * orig_gpr2 is stored on the kernel stack
- */
- tmp = *(__u32*)((addr_t) &task_pt_regs(child)->orig_gpr2 + 4);
+#ifdef CONFIG_COMPAT
+static int
+s390_genregs_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ int ret = 0;
+
+ /* Fake a 31 bit psw mask. */
+ if (count > 0 && pos == PT_PSWMASK / 2) {
+ u32 pswmask = PSW32_MASK_MERGE(psw32_user_bits,
+ (u32) (regs->psw.mask >> 32));
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK / 2,
+ PT_PSWADDR / 2);
+ }
- } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
- /*
- * floating point regs. are stored in the thread structure
- */
- offset = addr - (addr_t) &dummy32->regs.fp_regs;
- tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset);
+ /* Fake a 31 bit psw address. */
+ if (ret == 0 && count > 0 && pos == PT_PSWADDR / 2) {
+ u32 pswaddr = (u32) regs->psw.addr | PSW32_ADDR_AMODE31;
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &pswaddr, PT_PSWADDR / 2,
+ PT_GPR0 / 2);
+ }
- } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
- /*
- * per_info is found in the thread structure
- */
- offset = addr - (addr_t) &dummy32->regs.per_info;
- /* This is magic. See per_struct and per_struct32. */
- if ((offset >= (addr_t) &dummy_per32->control_regs &&
- offset < (addr_t) (&dummy_per32->control_regs + 1)) ||
- (offset >= (addr_t) &dummy_per32->starting_addr &&
- offset <= (addr_t) &dummy_per32->ending_addr) ||
- offset == (addr_t) &dummy_per32->lowcore.words.address)
- offset = offset*2 + 4;
+ /* The GPRs are directly on the stack. Just truncate them. */
+ while (ret == 0 && count > 0 && pos < PT_ACR0 / 2) {
+ u32 value = regs->gprs[(pos - PT_GPR0 / 2) / sizeof(u32)];
+ if (kbuf) {
+ *(u32 *) kbuf = value;
+ kbuf += sizeof(u32);
+ }
+ else if (put_user(value, (u32 __user *) ubuf))
+ ret = -EFAULT;
else
- offset = offset*2;
- tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset);
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
+ }
- } else
- tmp = 0;
+ /* The ACRs are kept in the thread_struct. */
+ if (ret == 0 && count > 0 && pos < PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE) {
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0 / 2,
+ PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE);
+ }
- return put_user(tmp, (__u32 __user *) data);
+ /* Finally, the ORIG_GPR2 value. */
+ if (count > 0) {
+ if (kbuf)
+ *(u32 *) kbuf = regs->orig_gpr2;
+ else if (put_user((u32) regs->orig_gpr2,
+ (u32 __user *) ubuf))
+ return -EFAULT;
+ }
+
+ return 0;
}
-/*
- * Same as poke_user but for a 31 bit program.
- */
static int
-poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
-{
- struct user32 *dummy32 = NULL;
- per_struct32 *dummy_per32 = NULL;
- addr_t offset;
- __u32 tmp;
+s390_genregs_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ int ret = 0;
+
+ /* Check for an invalid PSW mask. */
+ if (count > 0 && pos == PT_PSWMASK / 2) {
+ u32 pswmask;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &pswmask, PT_PSWMASK / 2,
+ PT_PSWADDR / 2);
+ if (ret)
+ return ret;
- if (!test_thread_flag(TIF_31BIT) ||
- (addr & 3) || addr > sizeof(struct user32) - 3)
- return -EIO;
+ if (pswmask != PSW_MASK_MERGE(psw_user32_bits, pswmask))
+ /* Invalid psw mask. */
+ return -EINVAL;
- tmp = (__u32) data;
+ /* Build a 64 bit psw mask from 31 bit mask. */
+ regs->psw.mask = PSW_MASK_MERGE(psw_user32_bits,
+ (u64) pswmask << 32);
+ FixPerRegisters(target);
+ }
- if (addr < (addr_t) &dummy32->regs.acrs) {
- /*
- * psw, gprs, acrs and orig_gpr2 are stored on the stack
- */
- if (addr == (addr_t) &dummy32->regs.psw.mask) {
+ /* Build a 64 bit psw address from 31 bit address. */
+ if (count > 0 && pos == PT_PSWADDR / 2) {
+ u32 pswaddr;
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &pswaddr, PT_PSWADDR / 2,
+ PT_GPR0 / 2);
+ if (ret == 0)
/* Build a 64 bit psw mask from 31 bit mask. */
- if (tmp != PSW32_MASK_MERGE(psw32_user_bits, tmp))
- /* Invalid psw mask. */
- return -EINVAL;
- task_pt_regs(child)->psw.mask =
- PSW_MASK_MERGE(psw_user32_bits, (__u64) tmp << 32);
- } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
- /* Build a 64 bit psw address from 31 bit address. */
- task_pt_regs(child)->psw.addr =
- (__u64) tmp & PSW32_ADDR_INSN;
- } else {
- /* gpr 0-15 */
- *(__u32*)((addr_t) &task_pt_regs(child)->psw
- + addr*2 + 4) = tmp;
+ regs->psw.addr = pswaddr & PSW32_ADDR_INSN;
+ }
+
+ /* The GPRs are directly onto the stack. */
+ while (ret == 0 && count > 0 && pos < PT_ACR0 / 2) {
+ u32 value;
+
+ if (kbuf) {
+ value = *(const u32 *) kbuf;
+ kbuf += sizeof(u32);
}
- } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) {
- /*
- * access registers are stored in the thread structure
- */
- offset = addr - (addr_t) &dummy32->regs.acrs;
- *(__u32*)((addr_t) &child->thread.acrs + offset) = tmp;
+ else if (get_user(value, (const u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
- } else if (addr == (addr_t) (&dummy32->regs.orig_gpr2)) {
- /*
- * orig_gpr2 is stored on the kernel stack
- */
- *(__u32*)((addr_t) &task_pt_regs(child)->orig_gpr2 + 4) = tmp;
+ regs->gprs[(pos - PT_GPR0 / 2) / sizeof(u32)] = value;
+ }
- } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
- /*
- * floating point regs. are stored in the thread structure
- */
- if (addr == (addr_t) &dummy32->regs.fp_regs.fpc &&
- (tmp & ~FPC_VALID_MASK) != 0)
- /* Invalid floating point control. */
- return -EINVAL;
- offset = addr - (addr_t) &dummy32->regs.fp_regs;
- *(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp;
+ /* The ACRs are kept in the thread_struct. */
+ if (count > 0 && pos < PT_ORIGGPR2 / 2) {
+ if (target == current
+ && (pos != PT_ACR0 / 2
+ || count < sizeof(target->thread.acrs)))
+ save_access_regs(target->thread.acrs);
+
+ ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.acrs,
+ PT_ACR0 / 2,
+ PT_ACR0 / 2 + NUM_ACRS * ACR_SIZE);
- } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
- /*
- * per_info is found in the thread structure.
- */
- offset = addr - (addr_t) &dummy32->regs.per_info;
- /*
- * This is magic. See per_struct and per_struct32.
- * By incident the offsets in per_struct are exactly
- * twice the offsets in per_struct32 for all fields.
- * The 8 byte fields need special handling though,
- * because the second half (bytes 4-7) is needed and
- * not the first half.
- */
- if ((offset >= (addr_t) &dummy_per32->control_regs &&
- offset < (addr_t) (&dummy_per32->control_regs + 1)) ||
- (offset >= (addr_t) &dummy_per32->starting_addr &&
- offset <= (addr_t) &dummy_per32->ending_addr) ||
- offset == (addr_t) &dummy_per32->lowcore.words.address)
- offset = offset*2 + 4;
- else
- offset = offset*2;
- *(__u32 *)((addr_t) &child->thread.per_info + offset) = tmp;
+ if (ret == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+ }
+ /* Finally, the ORIG_GPR2 value. */
+ if (ret == 0 && count > 0) {
+ u32 value;
+ if (kbuf)
+ value = *(const u32 *) kbuf;
+ else if (get_user(value, (const u32 __user *) ubuf))
+ return -EFAULT;
+ regs->orig_gpr2 = value;
}
- FixPerRegisters(child);
- return 0;
+ return ret;
}
-static int
-do_ptrace_emu31(struct task_struct *child, long request, long addr, long data)
+
+/*
+ * This is magic. See per_struct and per_struct32.
+ * By incident the offsets in per_struct are exactly
+ * twice the offsets in per_struct32 for all fields.
+ * The 8 byte fields need special handling though,
+ * because the second half (bytes 4-7) is needed and
+ * not the first half.
+ */
+static unsigned int
+offset_from_per32(unsigned int offset)
{
- unsigned int tmp; /* 4 bytes !! */
- ptrace_area_emu31 parea;
- int copied, ret;
-
- switch (request) {
- case PTRACE_PEEKTEXT:
- case PTRACE_PEEKDATA:
- /* read word at location addr. */
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- if (copied != sizeof(tmp))
- return -EIO;
- return put_user(tmp, (unsigned int __force __user *) data);
-
- case PTRACE_PEEKUSR:
- /* read the word at location addr in the USER area. */
- return peek_user_emu31(child, addr, data);
-
- case PTRACE_POKETEXT:
- case PTRACE_POKEDATA:
- /* write the word at location addr. */
- tmp = data;
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 1);
- if (copied != sizeof(tmp))
- return -EIO;
- return 0;
-
- case PTRACE_POKEUSR:
- /* write the word at location addr in the USER area */
- return poke_user_emu31(child, addr, data);
-
- case PTRACE_PEEKUSR_AREA:
- case PTRACE_POKEUSR_AREA:
- if (copy_from_user(&parea, (void __force __user *) addr,
- sizeof(parea)))
- return -EFAULT;
- addr = parea.kernel_addr;
- data = parea.process_addr;
- copied = 0;
- while (copied < parea.len) {
- if (request == PTRACE_PEEKUSR_AREA)
- ret = peek_user_emu31(child, addr, data);
- else {
- __u32 utmp;
- if (get_user(utmp,
- (__u32 __force __user *) data))
- return -EFAULT;
- ret = poke_user_emu31(child, addr, utmp);
- }
- if (ret)
- return ret;
- addr += sizeof(unsigned int);
- data += sizeof(unsigned int);
- copied += sizeof(unsigned int);
+ BUILD_BUG_ON(offsetof(per_struct32, control_regs) != 0);
+ if (offset - offsetof(per_struct32, control_regs) < 3*sizeof(u32)
+ || (offset >= offsetof(per_struct32, starting_addr) &&
+ offset <= offsetof(per_struct32, ending_addr))
+ || offset == offsetof(per_struct32, lowcore.words.address))
+ offset = offset*2 + 4;
+ else
+ offset = offset*2;
+ return offset;
+}
+
+static int
+s390_per_info_get(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ while (count > 0) {
+ u32 val = *(u32 *) ((char *) &target->thread.per_info
+ + offset_from_per32 (pos));
+ if (kbuf) {
+ *(u32 *) kbuf = val;
+ kbuf += sizeof(u32);
}
- return 0;
-#if 0 /* XXX */
- case PTRACE_GETEVENTMSG:
- return put_user((__u32) child->ptrace_message,
- (unsigned int __force __user *) data);
- case PTRACE_GETSIGINFO:
- if (child->last_siginfo == NULL)
- return -EINVAL;
- return copy_siginfo_to_user32((compat_siginfo_t
- __force __user *) data,
- child->last_siginfo);
- case PTRACE_SETSIGINFO:
- if (child->last_siginfo == NULL)
- return -EINVAL;
- return copy_siginfo_from_user32(child->last_siginfo,
- (compat_siginfo_t
- __force __user *) data);
+ else if (put_user(val, (u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
}
- return ptrace_request(child, request, addr, data);
+ return 0;
}
-#endif
-
-#define PT32_IEEE_IP 0x13c
static int
-do_ptrace(struct task_struct *child, long request, long addr, long data)
-{
- int ret;
-
- if (request == PTRACE_ATTACH)
- return ptrace_attach(child);
+s390_per_info_set(struct task_struct *target,
+ const struct utrace_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ while (count > 0) {
+ u32 val;
+
+ if (kbuf) {
+ val = *(const u32 *) kbuf;
+ kbuf += sizeof(u32);
+ }
+ else if (get_user(val, (const u32 __user *) ubuf))
+ return -EFAULT;
+ else
+ ubuf += sizeof(u32);
+ pos += sizeof(u32);
+ count -= sizeof(u32);
- /*
- * Special cases to get/store the ieee instructions pointer.
- */
- if (child == current) {
- if (request == PTRACE_PEEKUSR && addr == PT_IEEE_IP)
- return peek_user(child, addr, data);
- if (request == PTRACE_POKEUSR && addr == PT_IEEE_IP)
- return poke_user(child, addr, data);
-#ifdef CONFIG_COMPAT
- if (request == PTRACE_PEEKUSR &&
- addr == PT32_IEEE_IP && test_thread_flag(TIF_31BIT))
- return peek_user_emu31(child, addr, data);
- if (request == PTRACE_POKEUSR &&
- addr == PT32_IEEE_IP && test_thread_flag(TIF_31BIT))
- return poke_user_emu31(child, addr, data);
-#endif
+ *(u32 *) ((char *) &target->thread.per_info
+ + offset_from_per32 (pos)) = val;
}
+ return 0;
+}
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0)
- return ret;
-
- switch (request) {
- case PTRACE_SYSCALL:
- /* continue and stop at next (return from) syscall */
- case PTRACE_CONT:
- /* restart after signal. */
- if (!valid_signal(data))
- return -EIO;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- tracehook_disable_single_step(child);
- wake_up_process(child);
- return 0;
-
- case PTRACE_KILL:
- /*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- return 0;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- tracehook_disable_single_step(child);
- wake_up_process(child);
- return 0;
-
- case PTRACE_SINGLESTEP:
- /* set the trap flag. */
- if (!valid_signal(data))
- return -EIO;
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
- if (data)
- set_tsk_thread_flag(child, TIF_SINGLE_STEP);
- else
- tracehook_enable_single_step(child);
- /* give it a chance to run. */
- wake_up_process(child);
- return 0;
-
- case PTRACE_DETACH:
- /* detach a process that was attached. */
- return ptrace_detach(child, data);
+static const struct utrace_regset s390_compat_regsets[] = {
+ {
+ .size = sizeof(u32), .align = sizeof(u32),
+ .n = sizeof(s390_regs) / sizeof(long),
+ .get = s390_genregs_get, .set = s390_genregs_set
+ },
+ {
+ .size = sizeof(u32), .align = sizeof(u32),
+ .n = sizeof(s390_fp_regs) / sizeof(u32),
+ .get = fpregs_get, .set = fpregs_set
+ },
+ {
+ .size = sizeof(u32), .align = sizeof(u32),
+ .n = sizeof(per_struct) / sizeof(u32),
+ .get = s390_per_info_get, .set = s390_per_info_set
+ },
+};
+
+static const struct utrace_regset_view utrace_s390_compat_view = {
+ .name = "s390", .e_machine = EM_S390,
+ .regsets = s390_compat_regsets, .n = ARRAY_SIZE(s390_compat_regsets)
+};
+#endif /* CONFIG_COMPAT */
- /* Do requests that differ for 31/64 bit */
- default:
+const struct utrace_regset_view *utrace_native_view(struct task_struct *tsk)
+{
#ifdef CONFIG_COMPAT
- if (test_thread_flag(TIF_31BIT))
- return do_ptrace_emu31(child, request, addr, data);
+ if (test_tsk_thread_flag(tsk, TIF_31BIT))
+ return &utrace_s390_compat_view;
#endif
- return do_ptrace_normal(child, request, addr, data);
- }
- /* Not reached. */
- return -EIO;
+ return &utrace_s390_native_view;
}
-asmlinkage long
-sys_ptrace(long request, long pid, long addr, long data)
-{
- struct task_struct *child;
- int ret;
-
- lock_kernel();
- if (request == PTRACE_TRACEME) {
- ret = ptrace_traceme();
- goto out;
- }
-
- child = ptrace_get_task_struct(pid);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- goto out;
- }
-
- ret = do_ptrace(child, request, addr, data);
- put_task_struct(child);
-out:
- unlock_kernel();
- return ret;
-}
asmlinkage void
syscall_trace(struct pt_regs *regs, int entryexit)
linux-2.6-utrace-regset-sparc64.patch:
Makefile | 2
ptrace.c | 631 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
systbls.S | 4
3 files changed, 568 insertions(+), 69 deletions(-)
Index: linux-2.6-utrace-regset-sparc64.patch
===================================================================
RCS file: linux-2.6-utrace-regset-sparc64.patch
diff -N linux-2.6-utrace-regset-sparc64.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-regset-sparc64.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,726 @@
+[PATCH 2b] utrace: sparc64 regset support
+
+This patch converts the machine-dependent ptrace code into utrace regset
+support for sparc64.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+
+---
+
+ arch/sparc64/kernel/Makefile | 2
+ arch/sparc64/kernel/ptrace.c | 631 +++++++++++++++++++++++++++++++++++++----
+ arch/sparc64/kernel/systbls.S | 4
+ 3 files changed, 568 insertions(+), 69 deletions(-)
+
+--- linux-2.6/arch/sparc64/kernel/Makefile
++++ linux-2.6/arch/sparc64/kernel/Makefile
+@@ -5,6 +5,8 @@
+ EXTRA_AFLAGS := -ansi
+ EXTRA_CFLAGS := -Werror
+
++CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
++
+ extra-y := head.o init_task.o vmlinux.lds
+
+ obj-y := process.o setup.o cpu.o idprom.o \
+--- linux-2.6/arch/sparc64/kernel/ptrace.c
++++ linux-2.6/arch/sparc64/kernel/ptrace.c
+@@ -1,6 +1,6 @@
+-/* ptrace.c: Sparc process tracing support.
++/* ptrace.c: Sparc64 process tracing support.
+ *
+- * Copyright (C) 1996 David S. Miller (davem at caipfs.rutgers.edu)
++ * Copyright (C) 1996, 2006 David S. Miller (davem at davemloft.net)
+ * Copyright (C) 1997 Jakub Jelinek (jj at sunsite.mff.cuni.cz)
+ *
+ * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
+@@ -11,106 +11,603 @@
+ */
+
+ #include <linux/kernel.h>
++#include <linux/module.h>
+ #include <linux/sched.h>
+ #include <linux/mm.h>
+-#include <linux/errno.h>
+-#include <linux/ptrace.h>
+-#include <linux/user.h>
+-#include <linux/smp.h>
+-#include <linux/smp_lock.h>
+ #include <linux/security.h>
+ #include <linux/seccomp.h>
+ #include <linux/audit.h>
+-#include <linux/signal.h>
+ #include <linux/tracehook.h>
++#include <linux/elf.h>
++#include <linux/ptrace.h>
+
+ #include <asm/asi.h>
+ #include <asm/pgtable.h>
+ #include <asm/system.h>
+-#include <asm/uaccess.h>
+-#include <asm/psrcompat.h>
+-#include <asm/visasm.h>
+ #include <asm/spitfire.h>
+ #include <asm/page.h>
+ #include <asm/cpudata.h>
++#include <asm/psrcompat.h>
+
+-#if 0 /* XXX */
+-/* Returning from ptrace is a bit tricky because the syscall return
+- * low level code assumes any value returned which is negative and
+- * is a valid errno will mean setting the condition codes to indicate
+- * an error return. This doesn't work, so we have this hook.
++#define GENREG_G0 0
++#define GENREG_O0 8
++#define GENREG_L0 16
++#define GENREG_I0 24
++#define GENREG_TSTATE 32
++#define GENREG_TPC 33
++#define GENREG_TNPC 34
++#define GENREG_Y 35
++
++#define SPARC64_NGREGS 36
++
++static int genregs_get(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ void *kbuf, void __user *ubuf)
++{
++ struct pt_regs *regs = task_pt_regs(target);
++ int err;
++
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->u_regs,
++ GENREG_G0 * 8, GENREG_L0 * 8);
++
++ if (err == 0 && count > 0 && pos < (GENREG_TSTATE * 8)) {
++ struct thread_info *t = task_thread_info(target);
++ unsigned long rwindow[16], fp, *win;
++ int wsaved;
++
++ if (target == current)
++ flushw_user();
++
++ wsaved = __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_WSAVED];
++ fp = regs->u_regs[UREG_FP] + STACK_BIAS;
++ if (wsaved && t->rwbuf_stkptrs[wsaved - 1] == fp)
++ win = &t->reg_window[wsaved - 1].locals[0];
++ else {
++ if (target == current) {
++ if (copy_from_user(rwindow,
++ (void __user *) fp,
++ 16 * sizeof(long)))
++ err = -EFAULT;
++ } else
++ err = access_process_vm(target, fp, rwindow,
++ 16 * sizeof(long), 0);
++ if (err)
++ return err;
++ win = rwindow;
++ }
++
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
++ win, GENREG_L0 * 8,
++ GENREG_TSTATE * 8);
++ }
++
++ if (err == 0)
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
++ ®s->tstate, GENREG_TSTATE * 8,
++ GENREG_Y * 8);
++ if (err == 0 && count > 0) {
++ if (kbuf)
++ *(unsigned long *) kbuf = regs->y;
++ else if (put_user(regs->y, (unsigned long __user *) ubuf))
++ return -EFAULT;
++ }
++
++ return err;
++}
++
++/* Consistent with signal handling, we only allow userspace to
++ * modify the %asi, %icc, and %xcc fields of the %tstate register.
+ */
+-static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
++#define TSTATE_DEBUGCHANGE (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)
++
++static int genregs_set(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ const void *kbuf, const void __user *ubuf)
+ {
+- regs->u_regs[UREG_I0] = error;
+- regs->tstate |= (TSTATE_ICARRY | TSTATE_XCARRY);
+- regs->tpc = regs->tnpc;
+- regs->tnpc += 4;
++ struct pt_regs *regs = task_pt_regs(target);
++ unsigned long tstate_save;
++ int err;
++
++ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, regs->u_regs,
++ GENREG_G0 * 8, GENREG_L0 * 8);
++
++ if (err == 0 && count > 0 && pos < (GENREG_TSTATE * 8)) {
++ unsigned long fp = regs->u_regs[UREG_FP] + STACK_BIAS;
++ unsigned long rwindow[16], *winbuf;
++ unsigned int copy = (GENREG_TSTATE * 8) - pos;
++ unsigned int off;
++ int err;
++
++ if (target == current)
++ flushw_user();
++
++ if (count < copy)
++ copy = count;
++ off = pos - (GENREG_L0 * 8);
++
++ if (kbuf) {
++ winbuf = (unsigned long *) kbuf;
++ kbuf += copy;
++ }
++ else {
++ winbuf = rwindow;
++ if (copy_from_user(winbuf, ubuf, copy))
++ return -EFAULT;
++ ubuf += copy;
++ }
++ count -= copy;
++ pos += copy;
++
++ if (target == current)
++ err = copy_to_user((void __user *) fp + off,
++ winbuf, copy);
++ else
++ err = access_process_vm(target, fp + off,
++ winbuf, copy, 1);
++ }
++
++ tstate_save = regs->tstate &~ TSTATE_DEBUGCHANGE;
++
++ if (err == 0)
++ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
++ ®s->tstate, GENREG_TSTATE * 8,
++ GENREG_Y * 8);
++
++ regs->tstate &= TSTATE_DEBUGCHANGE;
++ regs->tstate |= tstate_save;
++
++ if (err == 0 && count > 0) {
++ if (kbuf)
++ regs->y = *(unsigned long *) kbuf;
++ else if (get_user(regs->y, (unsigned long __user *) ubuf))
++ return -EFAULT;
++ }
++
++ return err;
++}
++
++#define FPREG_F0 0
++#define FPREG_FSR 32
++#define FPREG_GSR 33
++#define FPREG_FPRS 34
++
++#define SPARC64_NFPREGS 35
++
++static int fpregs_get(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ void *kbuf, void __user *ubuf)
++{
++ struct thread_info *t = task_thread_info(target);
++ int err;
++
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
++ t->fpregs, FPREG_F0 * 8, FPREG_FSR * 8);
++
++ if (err == 0)
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
++ &t->xfsr[0], FPREG_FSR * 8,
++ FPREG_GSR * 8);
++
++ if (err == 0)
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
++ &t->gsr[0], FPREG_GSR * 8,
++ FPREG_FPRS * 8);
++
++ if (err == 0 && count > 0) {
++ struct pt_regs *regs = task_pt_regs(target);
++
++ if (kbuf)
++ *(unsigned long *) kbuf = regs->fprs;
++ else if (put_user(regs->fprs, (unsigned long __user *) ubuf))
++ return -EFAULT;
++ }
++
++ return err;
+ }
+
+-static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
++static int fpregs_set(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ const void *kbuf, const void __user *ubuf)
+ {
+- regs->u_regs[UREG_I0] = value;
+- regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
+- regs->tpc = regs->tnpc;
+- regs->tnpc += 4;
++ struct thread_info *t = task_thread_info(target);
++ int err;
++
++ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
++ t->fpregs, FPREG_F0 * 8, FPREG_FSR * 8);
++
++ if (err == 0)
++ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
++ &t->xfsr[0], FPREG_FSR * 8,
++ FPREG_GSR * 8);
++
++ if (err == 0)
++ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
++ &t->gsr[0], FPREG_GSR * 8,
++ FPREG_FPRS * 8);
++
++ if (err == 0 && count > 0) {
++ struct pt_regs *regs = task_pt_regs(target);
++
++ if (kbuf)
++ regs->fprs = *(unsigned long *) kbuf;
++ else if (get_user(regs->fprs, (unsigned long __user *) ubuf))
++ return -EFAULT;
++ }
++
++ return err;
+ }
+
+-static inline void
+-pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr)
++static const struct utrace_regset native_regsets[] = {
++ {
++ .n = SPARC64_NGREGS,
++ .size = sizeof(long), .align = sizeof(long),
++ .get = genregs_get, .set = genregs_set
++ },
++ {
++ .n = SPARC64_NFPREGS,
++ .size = sizeof(long), .align = sizeof(long),
++ .get = fpregs_get, .set = fpregs_set
++ },
++};
++
++static const struct utrace_regset_view utrace_sparc64_native_view = {
++ .name = UTS_MACHINE, .e_machine = ELF_ARCH,
++ .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
++};
++
++#ifdef CONFIG_COMPAT
++
++#define GENREG32_G0 0
++#define GENREG32_O0 8
++#define GENREG32_L0 16
++#define GENREG32_I0 24
++#define GENREG32_PSR 32
++#define GENREG32_PC 33
++#define GENREG32_NPC 34
++#define GENREG32_Y 35
++#define GENREG32_WIM 36
++#define GENREG32_TBR 37
++
++#define SPARC32_NGREGS 38
++
++static int genregs32_get(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ void *kbuf, void __user *ubuf)
+ {
+- if (test_thread_flag(TIF_32BIT)) {
+- if (put_user(value, (unsigned int __user *) addr)) {
+- pt_error_return(regs, EFAULT);
+- return;
+- }
+- } else {
+- if (put_user(value, (long __user *) addr)) {
+- pt_error_return(regs, EFAULT);
+- return;
++ struct pt_regs *regs = task_pt_regs(target);
++
++ while (count > 0 && pos < (GENREG32_L0 * 4)) {
++ u32 val = regs->u_regs[(pos - (GENREG32_G0*4))/sizeof(u32)];
++ if (kbuf) {
++ *(u32 *) kbuf = val;
++ kbuf += sizeof(u32);
++ } else if (put_user(val, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos < (GENREG32_PSR * 4)) {
++ struct thread_info *t = task_thread_info(target);
++ unsigned long fp;
++ u32 rwindow[16];
++ int wsaved;
++
++ if (target == current)
++ flushw_user();
++
++ wsaved = __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_WSAVED];
++ fp = regs->u_regs[UREG_FP] & 0xffffffffUL;
++ if (wsaved && t->rwbuf_stkptrs[wsaved - 1] == fp) {
++ int i;
++ for (i = 0; i < 8; i++)
++ rwindow[i + 0] =
++ t->reg_window[wsaved-1].locals[i];
++ for (i = 0; i < 8; i++)
++ rwindow[i + 8] =
++ t->reg_window[wsaved-1].ins[i];
++ } else {
++ int err;
++
++ if (target == current) {
++ err = 0;
++ if (copy_from_user(rwindow, (void __user *) fp,
++ 16 * sizeof(u32)))
++ err = -EFAULT;
++ } else
++ err = access_process_vm(target, fp, rwindow,
++ 16 * sizeof(u32), 0);
++ if (err)
++ return err;
++ }
++
++ while (count > 0 && pos < (GENREG32_PSR * 4)) {
++ u32 val = rwindow[(pos - (GENREG32_L0*4))/sizeof(u32)];
++
++ if (kbuf) {
++ *(u32 *) kbuf = val;
++ kbuf += sizeof(u32);
++ } else if (put_user(val, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
+ }
+ }
+- regs->u_regs[UREG_I0] = 0;
+- regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
+- regs->tpc = regs->tnpc;
+- regs->tnpc += 4;
++
++ if (count > 0 && pos == (GENREG32_PSR * 4)) {
++ u32 psr = tstate_to_psr(regs->tstate);
++
++ if (kbuf) {
++ *(u32 *) kbuf = psr;
++ kbuf += sizeof(u32);
++ } else if (put_user(psr, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos == (GENREG32_PC * 4)) {
++ u32 val = regs->tpc;
++
++ if (kbuf) {
++ *(u32 *) kbuf = val;
++ kbuf += sizeof(u32);
++ } else if (put_user(val, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos == (GENREG32_NPC * 4)) {
++ u32 val = regs->tnpc;
++
++ if (kbuf) {
++ *(u32 *) kbuf = val;
++ kbuf += sizeof(u32);
++ } else if (put_user(val, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos == (GENREG32_Y * 4)) {
++ if (kbuf) {
++ *(u32 *) kbuf = regs->y;
++ kbuf += sizeof(u32);
++ } else if (put_user(regs->y, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0) {
++ if (kbuf)
++ memset(kbuf, 0, count);
++ else if (clear_user(ubuf, count))
++ return -EFAULT;
++ }
++
++ return 0;
++}
++
++static int genregs32_set(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ const void *kbuf, const void __user *ubuf)
++{
++ struct pt_regs *regs = task_pt_regs(target);
++
++ while (count > 0 && pos < (GENREG32_L0 * 4)) {
++ unsigned long *loc;
++ loc = ®s->u_regs[(pos - (GENREG32_G0*4))/sizeof(u32)];
++ if (kbuf) {
++ *loc = *(u32 *) kbuf;
++ kbuf += sizeof(u32);
++ } else if (get_user(*loc, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos < (GENREG32_PSR * 4)) {
++ unsigned long fp;
++ u32 regbuf[16];
++ unsigned int off, copy;
++ int err;
++
++ if (target == current)
++ flushw_user();
++
++ copy = (GENREG32_PSR * 4) - pos;
++ if (count < copy)
++ copy = count;
++ BUG_ON(copy > 16 * sizeof(u32));
++
++ fp = regs->u_regs[UREG_FP] & 0xffffffffUL;
++ off = pos - (GENREG32_L0 * 4);
++ if (kbuf) {
++ memcpy(regbuf, kbuf, copy);
++ kbuf += copy;
++ } else if (copy_from_user(regbuf, ubuf, copy))
++ return -EFAULT;
++ else
++ ubuf += copy;
++ pos += copy;
++ count -= copy;
++
++ if (target == current) {
++ err = 0;
++ if (copy_to_user((void __user *) fp + off,
++ regbuf, count))
++ err = -EFAULT;
++ } else
++ err = access_process_vm(target, fp + off,
++ regbuf, count, 1);
++ if (err)
++ return err;
++ }
++
++ if (count > 0 && pos == (GENREG32_PSR * 4)) {
++ unsigned long tstate, tstate_save;
++ u32 psr;
++
++ tstate_save = regs->tstate&~(TSTATE_ICC|TSTATE_XCC);
++
++ if (kbuf) {
++ psr = *(u32 *) kbuf;
++ kbuf += sizeof(u32);
++ } else if (get_user(psr, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++
++ tstate = psr_to_tstate_icc(psr);
++ regs->tstate = tstate_save | tstate;
++ }
++
++ if (count > 0 && pos == (GENREG32_PC * 4)) {
++ if (kbuf) {
++ regs->tpc = *(u32 *) kbuf;
++ kbuf += sizeof(u32);
++ } else if (get_user(regs->tpc, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos == (GENREG32_NPC * 4)) {
++ if (kbuf) {
++ regs->tnpc = *(u32 *) kbuf;
++ kbuf += sizeof(u32);
++ } else if (get_user(regs->tnpc, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ if (count > 0 && pos == (GENREG32_Y * 4)) {
++ if (kbuf) {
++ regs->y = *(u32 *) kbuf;
++ kbuf += sizeof(u32);
++ } else if (get_user(regs->y, (u32 __user *) ubuf))
++ return -EFAULT;
++ else
++ ubuf += sizeof(u32);
++ pos += sizeof(u32);
++ count -= sizeof(u32);
++ }
++
++ /* Ignore WIM and TBR */
++
++ return 0;
+ }
+
+-static void
+-pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr)
++#define FPREG32_F0 0
++#define FPREG32_FSR 32
++
++#define SPARC32_NFPREGS 33
++
++static int fpregs32_get(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ void *kbuf, void __user *ubuf)
+ {
+- if (current->personality == PER_SUNOS)
+- pt_succ_return (regs, val);
+- else
+- pt_succ_return_linux (regs, val, addr);
++ struct thread_info *t = task_thread_info(target);
++ int err;
++
++ err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
++ t->fpregs, FPREG32_F0 * 4,
++ FPREG32_FSR * 4);
++
++ if (err == 0 && count > 0) {
++ if (kbuf) {
++ *(u32 *) kbuf = t->xfsr[0];
++ } else if (put_user(t->xfsr[0], (u32 __user *) ubuf))
++ return -EFAULT;
++ }
++
++ return err;
+ }
+-#endif
+
+-/* #define ALLOW_INIT_TRACING */
+-/* #define DEBUG_PTRACE */
++static int fpregs32_set(struct task_struct *target,
++ const struct utrace_regset *regset,
++ unsigned int pos, unsigned int count,
++ const void *kbuf, const void __user *ubuf)
++{
++ struct thread_info *t = task_thread_info(target);
++ int err;
+
+-#ifdef DEBUG_PTRACE
+-char *pt_rq [] = {
+- /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
+- /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
+- /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
+- /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
+- /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
+- /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
+- /* 24 */ "SYSCALL", ""
++ err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
++ t->fpregs, FPREG32_F0 * 4,
++ FPREG32_FSR * 4);
++
++ if (err == 0 && count > 0) {
++ u32 fsr;
++ if (kbuf) {
++ fsr = *(u32 *) kbuf;
++ } else if (get_user(fsr, (u32 __user *) ubuf))
++ return -EFAULT;
++ t->xfsr[0] = (t->xfsr[0] & 0xffffffff00000000UL) | fsr;
++ }
++
++ return 0;
++}
++
++static const struct utrace_regset sparc32_regsets[] = {
++ {
++ .n = SPARC32_NGREGS,
++ .size = sizeof(u32), .align = sizeof(u32),
++ .get = genregs32_get, .set = genregs32_set
++ },
++ {
++ .n = SPARC32_NFPREGS,
++ .size = sizeof(u32), .align = sizeof(u32),
++ .get = fpregs32_get, .set = fpregs32_set
++ },
+ };
+-#endif
+
+-/*
+- * Called by kernel/ptrace.c when detaching..
+- *
+- * Make sure single step bits etc are not set.
+- */
+-void ptrace_disable(struct task_struct *child)
++static const struct utrace_regset_view utrace_sparc32_view = {
++ .name = "sparc", .e_machine = EM_SPARC,
++ .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
++};
++
++#endif /* CONFIG_COMPAT */
++
++const struct utrace_regset_view *utrace_native_view(struct task_struct *tsk)
+ {
+- /* nothing to do */
++#ifdef CONFIG_COMPAT
++ if (test_tsk_thread_flag(tsk, TIF_32BIT))
++ return &utrace_sparc32_view;
++#endif
++ return &utrace_sparc64_native_view;
+ }
+
++
+ /* To get the necessary page struct, access_process_vm() first calls
+ * get_user_pages(). This has done a flush_dcache_page() on the
+ * accessed page. Then our caller (copy_{to,from}_user_page()) did
+--- linux-2.6/arch/sparc64/kernel/systbls.S
++++ linux-2.6/arch/sparc64/kernel/systbls.S
+@@ -24,7 +24,7 @@ sys_call_table32:
+ /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_chown16, sys32_mknod
+ /*15*/ .word sys_chmod, sys32_lchown16, sparc_brk, sys32_perfctr, sys32_lseek
+ /*20*/ .word sys_getpid, sys_capget, sys_capset, sys32_setuid16, sys32_getuid16
+-/*25*/ .word sys32_vmsplice, sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause
++/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause
+ /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice
+ .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile
+ /*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid
+@@ -170,7 +170,7 @@ sunos_sys_table:
+ .word sys_chmod, sys32_lchown16, sunos_brk
+ .word sunos_nosys, sys32_lseek, sunos_getpid
+ .word sunos_nosys, sunos_nosys, sunos_nosys
+- .word sunos_getuid, sunos_nosys, sys_ptrace
++ .word sunos_getuid, sunos_nosys, compat_sys_ptrace
+ .word sunos_nosys, sunos_nosys, sunos_nosys
+ .word sunos_nosys, sunos_nosys, sunos_nosys
+ .word sys_access, sunos_nosys, sunos_nosys
linux-2.6-utrace-regset.patch:
arch/i386/kernel/i387.c | 143 +++---
arch/i386/kernel/ptrace.c | 822 ++++++++++++++++++++----------------
arch/powerpc/kernel/Makefile | 4
arch/powerpc/kernel/ptrace-common.h | 145 ------
arch/powerpc/kernel/ptrace.c | 718 +++++++++++++++----------------
arch/powerpc/kernel/ptrace32.c | 443 -------------------
arch/x86_64/ia32/fpu32.c | 92 +++-
arch/x86_64/ia32/ptrace32.c | 719 ++++++++++++++++++++-----------
arch/x86_64/kernel/ptrace.c | 730 +++++++++++++++++++------------
include/asm-i386/i387.h | 13
include/asm-x86_64/fpu32.h | 3
include/asm-x86_64/tracehook.h | 8
include/linux/tracehook.h | 244 ++++++++++
kernel/ptrace.c | 8
14 files changed, 2124 insertions(+), 1968 deletions(-)
View full diff with command:
/usr/bin/cvs -f diff -kk -u -N -r 1.2 -r 1.3 linux-2.6-utrace-regset.patch
Index: linux-2.6-utrace-regset.patch
===================================================================
RCS file: linux-2.6-utrace-regset.patch
diff -N linux-2.6-utrace-regset.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-regset.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,4713 @@
+[PATCH 2] utrace: register sets
+
+This provides a new uniform interface in <linux/tracehook.h> for accessing
+registers and similar per-thread machine resources. The old architecture
+ptrace code for accessing register state is rolled into new functions to
+flesh out the utrace_regset interface. Nothing yet uses this interface.
+The hope is that this interface can cover most of the machine-dependent
+issues for any higher-level tracing/debugging interface.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+
+---
+
+ arch/i386/kernel/i387.c | 143 +++---
+ arch/i386/kernel/ptrace.c | 822 ++++++++++++++++++++---------------
+ arch/x86_64/ia32/ptrace32.c | 719 ++++++++++++++++++++-----------
+ arch/x86_64/ia32/fpu32.c | 92 +++-
+ arch/x86_64/kernel/ptrace.c | 730 +++++++++++++++++++------------
+ arch/powerpc/kernel/Makefile | 4
+ arch/powerpc/kernel/ptrace32.c | 443 -------------------
+ arch/powerpc/kernel/ptrace.c | 718 +++++++++++++++----------------
+ arch/powerpc/kernel/ptrace-common.h | 145 ------
+ kernel/ptrace.c | 8
+ include/linux/tracehook.h | 244 ++++++++++
+ include/asm-i386/i387.h | 13 -
+ include/asm-x86_64/fpu32.h | 3
+ include/asm-x86_64/tracehook.h | 8
+ 14 files changed, 2124 insertions(+), 1968 deletions(-)
+ delete arch/powerpc/kernel/ptrace32.c
+ delete arch/powerpc/kernel/ptrace-common.h
+
+--- linux-2.6/arch/i386/kernel/i387.c
++++ linux-2.6/arch/i386/kernel/i387.c
+@@ -222,14 +222,10 @@ void set_fpu_twd( struct task_struct *ts
+ * FXSR floating point environment conversions.
+ */
+
+-static int convert_fxsr_to_user( struct _fpstate __user *buf,
+- struct i387_fxsave_struct *fxsave )
++static inline void
++convert_fxsr_env_to_i387(unsigned long env[7],
++ struct i387_fxsave_struct *fxsave)
+ {
+- unsigned long env[7];
+- struct _fpreg __user *to;
+- struct _fpxreg *from;
+- int i;
+-
+ env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
+ env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
+ env[2] = twd_fxsr_to_i387(fxsave);
+@@ -237,7 +233,17 @@ static int convert_fxsr_to_user( struct
+ env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
+ env[5] = fxsave->foo;
+ env[6] = fxsave->fos;
++}
++
++static int convert_fxsr_to_user(struct _fpstate __user *buf,
++ struct i387_fxsave_struct *fxsave)
++{
++ unsigned long env[7];
++ struct _fpreg __user *to;
++ struct _fpxreg *from;
++ int i;
+
++ convert_fxsr_env_to_i387(env, fxsave);
+ if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
+ return 1;
+
+@@ -255,6 +261,20 @@ static int convert_fxsr_to_user( struct
+ return 0;
+ }
+
++static inline void
++convert_fxsr_env_from_i387(struct i387_fxsave_struct *fxsave,
++ const unsigned long env[7])
++{
++ fxsave->cwd = (unsigned short)(env[0] & 0xffff);
++ fxsave->swd = (unsigned short)(env[1] & 0xffff);
++ fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
++ fxsave->fip = env[3];
++ fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
++ fxsave->fcs = (env[4] & 0xffff);
++ fxsave->foo = env[5];
++ fxsave->fos = env[6];
++}
++
+ static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
+ struct _fpstate __user *buf )
+ {
+@@ -266,14 +286,7 @@ static int convert_fxsr_from_user( struc
+ if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
+ return 1;
+
+- fxsave->cwd = (unsigned short)(env[0] & 0xffff);
+- fxsave->swd = (unsigned short)(env[1] & 0xffff);
+- fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
+- fxsave->fip = env[3];
+- fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
+- fxsave->fcs = (env[4] & 0xffff);
+- fxsave->foo = env[5];
+- fxsave->fos = env[6];
++ convert_fxsr_env_from_i387(fxsave, env);
+
+ to = (struct _fpxreg *) &fxsave->st_space[0];
+ from = &buf->_st[0];
+@@ -388,88 +401,82 @@ int restore_i387( struct _fpstate __user
+ * ptrace request handlers.
+ */
+
+-static inline int get_fpregs_fsave( struct user_i387_struct __user *buf,
+- struct task_struct *tsk )
++static inline void get_fpregs_fsave(struct user_i387_struct *buf,
++ struct task_struct *tsk)
+ {
+- return __copy_to_user( buf, &tsk->thread.i387.fsave,
+- sizeof(struct user_i387_struct) );
++ memcpy(buf, &tsk->thread.i387.fsave, sizeof(struct user_i387_struct));
+ }
+
+-static inline int get_fpregs_fxsave( struct user_i387_struct __user *buf,
+- struct task_struct *tsk )
++static inline void get_fpregs_fxsave(struct user_i387_struct *buf,
++ struct task_struct *tsk)
+ {
+- return convert_fxsr_to_user( (struct _fpstate __user *)buf,
+- &tsk->thread.i387.fxsave );
++ struct _fpreg *to;
++ const struct _fpxreg *from;
++ unsigned int i;
++
++ convert_fxsr_env_to_i387((unsigned long *) buf,
++ &tsk->thread.i387.fxsave);
++
++ to = (struct _fpreg *) buf->st_space;
++ from = (const struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
++ for (i = 0; i < 8; i++, to++, from++)
++ *to = *(const struct _fpreg *) from;
+ }
+
+-int get_fpregs( struct user_i387_struct __user *buf, struct task_struct *tsk )
++int get_fpregs(struct user_i387_struct *buf, struct task_struct *tsk)
+ {
+ if ( HAVE_HWFP ) {
+- if ( cpu_has_fxsr ) {
+- return get_fpregs_fxsave( buf, tsk );
+- } else {
+- return get_fpregs_fsave( buf, tsk );
+- }
++ if (cpu_has_fxsr)
++ get_fpregs_fxsave(buf, tsk);
++ else
++ get_fpregs_fsave(buf, tsk);
++ return 0;
+ } else {
+ return save_i387_soft( &tsk->thread.i387.soft,
+ (struct _fpstate __user *)buf );
+ }
+ }
+
+-static inline int set_fpregs_fsave( struct task_struct *tsk,
+- struct user_i387_struct __user *buf )
++static inline void set_fpregs_fsave(struct task_struct *tsk,
++ const struct user_i387_struct *buf)
+ {
+- return __copy_from_user( &tsk->thread.i387.fsave, buf,
+- sizeof(struct user_i387_struct) );
++ memcpy(&tsk->thread.i387.fsave, buf, sizeof(struct user_i387_struct));
+ }
+
+-static inline int set_fpregs_fxsave( struct task_struct *tsk,
+- struct user_i387_struct __user *buf )
++static inline void set_fpregs_fxsave(struct task_struct *tsk,
++ const struct user_i387_struct *buf)
+ {
+- return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
+- (struct _fpstate __user *)buf );
++ struct _fpxreg *to;
++ const struct _fpreg *from;
++ unsigned int i;
++
++ convert_fxsr_env_from_i387(&tsk->thread.i387.fxsave,
++ (unsigned long *) buf);
++
++ to = (struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
++ from = (const struct _fpreg *) buf->st_space;
++ for (i = 0; i < 8; i++, to++, from++)
++ *(struct _fpreg *) to = *from;
+ }
+
+-int set_fpregs( struct task_struct *tsk, struct user_i387_struct __user *buf )
++int set_fpregs(struct task_struct *tsk, const struct user_i387_struct *buf)
+ {
[...4320 lines suppressed...]
++ * @e_machine: ELF %EM_* value for which this is the native view, if any.
++ *
++ * A regset view is a collection of regsets (&struct utrace_regset,
++ * above). This describes all the state of a thread that can be seen
++ * from a given architecture/ABI environment. More than one view might
++ * refer to the same &struct utrace_regset, or more than one regset
++ * might refer to the same machine-specific state in the thread. For
++ * example, a 32-bit thread's state could be examined from the 32-bit
++ * view or from the 64-bit view. Either method reaches the same thread
++ * register state, doing appropriate widening or truncation.
++ */
++struct utrace_regset_view {
++ const char *name;
++ const struct utrace_regset *regsets;
++ unsigned int n;
++ u16 e_machine;
++};
++
++/*
++ * This is documented here rather than at the definition sites because its
++ * implementation is machine-dependent but its interface is universal.
++ */
++/**
++ * utrace_native_view - Return the process's native regset view.
++ * @tsk: a thread of the process in question
++ *
++ * Return the &struct utrace_regset_view that is native for the given process.
++ * For example, what it would access when it called ptrace().
++ * Throughout the life of the process, this only changes at exec.
++ */
++const struct utrace_regset_view *utrace_native_view(struct task_struct *tsk);
++
++
++/*
++ * These are helpers for writing regset get/set functions in arch code.
++ * Because @start_pos and @end_pos are always compile-time constants,
++ * these are inlined into very little code though they look large.
++ *
++ * Use one or more calls sequentially for each chunk of regset data stored
++ * contiguously in memory. Call with constants for @start_pos and @end_pos,
++ * giving the range of byte positions in the regset that data corresponds
++ * to; @end_pos can be -1 if this chunk is at the end of the regset layout.
++ * Each call updates the arguments to point past its chunk.
++ */
++
++static inline int
++utrace_regset_copyout(unsigned int *pos, unsigned int *count,
++ void **kbuf, void __user **ubuf,
++ const void *data, int start_pos, int end_pos)
++{
++ if (*count == 0)
++ return 0;
++ BUG_ON(*pos < start_pos);
++ if (end_pos < 0 || *pos < end_pos) {
++ unsigned int copy = (end_pos < 0 ? *count
++ : min(*count, end_pos - *pos));
++ data += *pos - start_pos;
++ if (*kbuf) {
++ memcpy(*kbuf, data, copy);
++ *kbuf += copy;
++ }
++ else if (copy_to_user(*ubuf, data, copy))
++ return -EFAULT;
++ else
++ *ubuf += copy;
++ *pos += copy;
++ *count -= copy;
++ }
++ return 0;
++}
++
++static inline int
++utrace_regset_copyin(unsigned int *pos, unsigned int *count,
++ const void **kbuf, const void __user **ubuf,
++ void *data, int start_pos, int end_pos)
++{
++ if (*count == 0)
++ return 0;
++ BUG_ON(*pos < start_pos);
++ if (end_pos < 0 || *pos < end_pos) {
++ unsigned int copy = (end_pos < 0 ? *count
++ : min(*count, end_pos - *pos));
++ data += *pos - start_pos;
++ if (*kbuf) {
++ memcpy(data, *kbuf, copy);
++ *kbuf += copy;
++ }
++ else if (copy_from_user(data, *ubuf, copy))
++ return -EFAULT;
++ else
++ *ubuf += copy;
++ *pos += copy;
++ *count -= copy;
++ }
++ return 0;
++}
++
++/*
++ * These two parallel the two above, but for portions of a regset layout
++ * that always read as all-zero or for which writes are ignored.
++ */
++static inline int
++utrace_regset_copyout_zero(unsigned int *pos, unsigned int *count,
++ void **kbuf, void __user **ubuf,
++ int start_pos, int end_pos)
++{
++ if (*count == 0)
++ return 0;
++ BUG_ON(*pos < start_pos);
++ if (end_pos < 0 || *pos < end_pos) {
++ unsigned int copy = (end_pos < 0 ? *count
++ : min(*count, end_pos - *pos));
++ if (*kbuf) {
++ memset(*kbuf, 0, copy);
++ *kbuf += copy;
++ }
++ else if (clear_user(*ubuf, copy))
++ return -EFAULT;
++ else
++ *ubuf += copy;
++ *pos += copy;
++ *count -= copy;
++ }
++ return 0;
++}
++
++static inline int
++utrace_regset_copyin_ignore(unsigned int *pos, unsigned int *count,
++ const void **kbuf, const void __user **ubuf,
++ int start_pos, int end_pos)
++{
++ if (*count == 0)
++ return 0;
++ BUG_ON(*pos < start_pos);
++ if (end_pos < 0 || *pos < end_pos) {
++ unsigned int copy = (end_pos < 0 ? *count
++ : min(*count, end_pos - *pos));
++ if (*kbuf)
++ *kbuf += copy;
++ else
++ *ubuf += copy;
++ *pos += copy;
++ *count -= copy;
++ }
++ return 0;
++}
++
++
+ /*
+ * Following are entry points from core code, where the user debugging
+ * support can affect the normal behavior. The locking situation is
+--- linux-2.6/include/asm-i386/i387.h
++++ linux-2.6/include/asm-i386/i387.h
+@@ -130,17 +130,12 @@ extern int save_i387( struct _fpstate __
+ extern int restore_i387( struct _fpstate __user *buf );
+
+ /*
+- * ptrace request handers...
++ * ptrace request handlers...
+ */
+-extern int get_fpregs( struct user_i387_struct __user *buf,
+- struct task_struct *tsk );
+-extern int set_fpregs( struct task_struct *tsk,
+- struct user_i387_struct __user *buf );
++extern int get_fpregs(struct user_i387_struct *, struct task_struct *);
++extern int set_fpregs(struct task_struct *, const struct user_i387_struct *);
++extern void updated_fpxregs(struct task_struct *tsk);
+
+-extern int get_fpxregs( struct user_fxsr_struct __user *buf,
+- struct task_struct *tsk );
+-extern int set_fpxregs( struct task_struct *tsk,
+- struct user_fxsr_struct __user *buf );
+
+ /*
+ * FPU state for core dumps...
+--- linux-2.6/include/asm-x86_64/fpu32.h
++++ linux-2.6/include/asm-x86_64/fpu32.h
+@@ -7,4 +7,7 @@ int restore_i387_ia32(struct task_struct
+ int save_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 __user *buf,
+ struct pt_regs *regs, int fsave);
+
++int get_fpregs32(struct user_i387_ia32_struct *, struct task_struct *);
++int set_fpregs32(struct task_struct *, const struct user_i387_ia32_struct *);
++
+ #endif
+--- linux-2.6/include/asm-x86_64/tracehook.h
++++ linux-2.6/include/asm-x86_64/tracehook.h
+@@ -48,4 +48,12 @@ static inline void tracehook_abort_sysca
+ regs->orig_rax = -1L;
+ }
+
++/*
++ * These are used directly by some of the regset code.
++ */
++extern const struct utrace_regset_view utrace_x86_64_native;
++#ifdef CONFIG_IA32_EMULATION
++extern const struct utrace_regset_view utrace_ia32_view;
++#endif
++
+ #endif
linux-2.6-utrace-tracehook-avr32.patch:
arch/avr32/kernel/entry-avr32b.S | 10 +++
arch/avr32/kernel/process.c | 2
arch/avr32/kernel/ptrace.c | 102 +++++++++------------------------------
include/asm-avr32/tracehook.h | 62 +++++++++++++++++++++++
4 files changed, 96 insertions(+), 80 deletions(-)
--- NEW FILE linux-2.6-utrace-tracehook-avr32.patch ---
[PATCH 1e] utrace: tracehook for AVR32
From: Haavard Skinnemoen <hskinnemoen at atmel.com>
This patch does the initial tracehook conversion for AVR32.
Signed-off-by: Haavard Skinnemoen <hskinnemoen at atmel.com>
Signed-off-by: Roland McGrath <roland at redhat.com>
---
arch/avr32/kernel/ptrace.c | 102 ++++++++++----------------------------
arch/avr32/kernel/process.c | 2 -
arch/avr32/kernel/entry-avr32b.S | 10 +++-
include/asm-avr32/tracehook.h | 62 +++++++++++++++++++++++
4 files changed, 96 insertions(+), 80 deletions(-)
create include/asm-avr32/tracehook.h
--- linux-2.6/arch/avr32/kernel/ptrace.c
+++ linux-2.6/arch/avr32/kernel/ptrace.c
@@ -5,20 +5,16 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#undef DEBUG
+#include <linux/compile.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/user.h>
-#include <linux/security.h>
-#include <linux/unistd.h>
-#include <linux/notifier.h>
-
-#include <asm/traps.h>
-#include <asm/uaccess.h>
-#include <asm/ocd.h>
+
#include <asm/mmu_context.h>
#include <linux/kdebug.h>
@@ -28,52 +24,7 @@ static struct pt_regs *get_user_regs(str
THREAD_SIZE - sizeof(struct pt_regs));
}
-static void ptrace_single_step(struct task_struct *tsk)
-{
- pr_debug("ptrace_single_step: pid=%u, SR=0x%08lx\n",
- tsk->pid, tsk->thread.cpu_context.sr);
- if (!(tsk->thread.cpu_context.sr & SR_D)) {
- /*
- * Set a breakpoint at the current pc to force the
- * process into debug mode. The syscall/exception
- * exit code will set a breakpoint at the return
- * address when this flag is set.
- */
- pr_debug("ptrace_single_step: Setting TIF_BREAKPOINT\n");
- set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
- }
-
- /* The monitor code will do the actual step for us */
- set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
-}
-
-/*
- * Called by kernel/ptrace.c when detaching
- *
- * Make sure any single step bits, etc. are not set
- */
-void ptrace_disable(struct task_struct *child)
-{
- clear_tsk_thread_flag(child, TIF_SINGLE_STEP);
-}
-
-/*
- * Handle hitting a breakpoint
- */
-static void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
-{
- siginfo_t info;
-
- info.si_signo = SIGTRAP;
- info.si_errno = 0;
- info.si_code = TRAP_BRKPT;
- info.si_addr = (void __user *)instruction_pointer(regs);
-
- pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
- tsk->pid, info.si_addr);
- force_sig_info(SIGTRAP, &info, tsk);
-}
-
+#if 0
/*
* Read the word at offset "offset" into the task's "struct user". We
* actually access the pt_regs struct stored on the kernel stack.
@@ -257,32 +208,31 @@ long arch_ptrace(struct task_struct *chi
pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC));
return ret;
}
+#endif
-asmlinkage void syscall_trace(void)
+asmlinkage void syscall_trace(struct pt_regs *regs, int is_exit)
{
- pr_debug("syscall_trace called\n");
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
- if (!(current->ptrace & PT_PTRACED))
- return;
- pr_debug("syscall_trace: notifying parent\n");
- /* The 0x80 provides a way for the tracing parent to
- * distinguish between a syscall stop and SIGTRAP delivery */
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
+ tracehook_report_syscall(regs, is_exit);
+}
- /*
- * this isn't the same as continuing with a signal, but it
- * will do for normal use. strace only continues with a
- * signal if the stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- pr_debug("syscall_trace: sending signal %d to PID %u\n",
- current->exit_code, current->pid);
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
+/*
+ * Handle hitting a breakpoint
+ */
+static void do_breakpoint(struct task_struct *tsk, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_BRKPT;
+ info.si_addr = (void __user *)instruction_pointer(regs);
+
+ pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
+ tsk->pid, info.si_addr);
+ force_sig_info(SIGTRAP, &info, tsk);
}
asmlinkage void do_debug_priv(struct pt_regs *regs)
@@ -361,10 +311,10 @@ asmlinkage void do_debug(struct pt_regs
__mtdr(DBGREG_DC, dc);
clear_thread_flag(TIF_SINGLE_STEP);
- ptrace_break(current, regs);
+ do_breakpoint(current, regs);
}
} else {
/* regular breakpoint */
- ptrace_break(current, regs);
+ do_breakpoint(current, regs);
}
}
--- linux-2.6/arch/avr32/kernel/process.c
+++ linux-2.6/arch/avr32/kernel/process.c
@@ -382,8 +382,6 @@ asmlinkage int sys_execve(char __user *u
goto out;
error = do_execve(filename, uargv, uenvp, regs);
- if (error == 0)
- current->ptrace &= ~PT_DTRACE;
putname(filename);
out:
--- linux-2.6/arch/avr32/kernel/entry-avr32b.S
+++ linux-2.6/arch/avr32/kernel/entry-avr32b.S
@@ -223,15 +223,21 @@ ret_from_fork:
rjmp syscall_exit_cont
syscall_trace_enter:
- pushm r8-r12
+ mov r12, sp /* regs */
+ mov r11, 0 /* is_exit */
rcall syscall_trace
- popm r8-r12
+
+ /* syscall_trace may update r8, so reload r8-r12 from regs. */
+ sub lr, sp, -REG_R12
+ ldm lr, r8-r12
rjmp syscall_trace_cont
syscall_exit_work:
bld r1, TIF_SYSCALL_TRACE
brcc 1f
unmask_interrupts
+ mov r12, sp
+ mov r11, 1
rcall syscall_trace
mask_interrupts
ld.w r1, r0[TI_flags]
--- linux-2.6/include/asm-avr32/tracehook.h
+++ linux-2.6/include/asm-avr32/tracehook.h
@@ -0,0 +1,62 @@
+/*
+ * Tracing hooks for AVR32
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * 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 Software Foundation.
+ */
+#ifndef _ASM_AVR32_TRACEHOOK_H
+#define _ASM_AVR32_TRACEHOOK_H
+
+#include <linux/sched.h>
+
+#define ARCH_HAS_SINGLE_STEP 1
+
+static inline void tracehook_enable_single_step(struct task_struct *tsk)
+{
+ /*
+ * If the process is stopped in debug mode, simply set
+ * TIF_SINGLE_STEP to tell the monitor code to set the single
+ * step bit in DC before returning.
+ *
+ * Otherwise, we need to set a breakpoint at the return
+ * address before returning to userspace. TIF_BREAKPOINT will
+ * tell the syscall/exception exit code to do this.
+ */
+ if (!(tsk->thread.cpu_context.sr & SR_D))
+ set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+
+ set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline void tracehook_disable_single_step(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+ clear_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+ return test_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ /* Invalid system call number => return -ENOSYS */
+ regs->r8 = -1;
+}
+
+
+#endif /* _ASM_AVR32_TRACEHOOK_H */
linux-2.6-utrace-tracehook-ia64.patch:
arch/ia64/ia32/ia32_entry.S | 2 -
arch/ia64/ia32/sys_ia32.c | 23 ++-----------
arch/ia64/kernel/ptrace.c | 39 +++++-----------------
arch/ia64/kernel/signal.c | 4 +-
include/asm-ia64/tracehook.h | 73 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 91 insertions(+), 50 deletions(-)
--- NEW FILE linux-2.6-utrace-tracehook-ia64.patch ---
[PATCH 1a] utrace: tracehook for ia64
This patch does the initial tracehook conversion for ia64.
Signed-off-by: Roland McGrath <roland at redhat.com>
Signed-off-by: Anil S Keshavamurthy <anil.s.keshavamurthy at intel.com>
Signed-off-by: Bibo mao <bibo.mao at intel.com>
---
arch/ia64/ia32/ia32_entry.S | 2 +
arch/ia64/ia32/sys_ia32.c | 23 ++-----------
arch/ia64/kernel/ptrace.c | 39 ++++++----------------
arch/ia64/kernel/signal.c | 4 ++
include/asm-ia64/tracehook.h | 73 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 91 insertions(+), 50 deletions(-)
create include/asm-ia64/tracehook.h
--- linux-2.6/arch/ia64/ia32/ia32_entry.S
+++ linux-2.6/arch/ia64/ia32/ia32_entry.S
@@ -199,7 +199,7 @@ ia32_syscall_table:
data8 sys_setuid /* 16-bit version */
data8 sys_getuid /* 16-bit version */
data8 compat_sys_stime /* 25 */
- data8 sys32_ptrace
+ data8 compat_sys_ptrace
data8 sys32_alarm
data8 sys_ni_syscall
data8 sys32_pause
--- linux-2.6/arch/ia64/ia32/sys_ia32.c
+++ linux-2.6/arch/ia64/ia32/sys_ia32.c
@@ -1436,25 +1436,6 @@ sys32_waitpid (int pid, unsigned int *st
return compat_sys_wait4(pid, stat_addr, options, NULL);
}
-static unsigned int
-ia32_peek (struct task_struct *child, unsigned long addr, unsigned int *val)
-{
- size_t copied;
- unsigned int ret;
-
- copied = access_process_vm(child, addr, val, sizeof(*val), 0);
- return (copied != sizeof(ret)) ? -EIO : 0;
-}
-
-static unsigned int
-ia32_poke (struct task_struct *child, unsigned long addr, unsigned int val)
-{
-
- if (access_process_vm(child, addr, &val, sizeof(val), 1) != sizeof(val))
- return -EIO;
- return 0;
-}
-
/*
* The order in which registers are stored in the ptrace regs structure
*/
@@ -1752,6 +1733,7 @@ restore_ia32_fpxstate (struct task_struc
return 0;
}
+#if 0 /* XXX */
asmlinkage long
sys32_ptrace (int request, pid_t pid, unsigned int addr, unsigned int data)
{
@@ -1859,9 +1841,11 @@ sys32_ptrace (int request, pid_t pid, un
compat_ptr(data));
break;
+#if 0 /* XXX */
case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message, (unsigned int __user *) compat_ptr(data));
break;
+#endif
case PTRACE_SYSCALL: /* continue, stop after next syscall */
case PTRACE_CONT: /* restart after signal. */
@@ -1882,6 +1866,7 @@ sys32_ptrace (int request, pid_t pid, un
unlock_kernel();
return ret;
}
+#endif
typedef struct {
unsigned int ss_sp;
--- linux-2.6/arch/ia64/kernel/ptrace.c
+++ linux-2.6/arch/ia64/kernel/ptrace.c
@@ -12,6 +12,7 @@
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/smp_lock.h>
#include <linux/user.h>
#include <linux/security.h>
@@ -1598,28 +1599,6 @@ sys_ptrace (long request, pid_t pid, uns
return ret;
}
-
-static void
-syscall_trace (void)
-{
- /*
- * The 0x80 provides a way for the tracing parent to
- * distinguish between a syscall stop and SIGTRAP delivery.
- */
- ptrace_notify(SIGTRAP
- | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
-
- /*
- * This isn't the same as continuing with a signal, but it
- * will do for normal use. strace only continues with a
- * signal if the stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
-}
-
/* "asmlinkage" so the input arguments are preserved... */
asmlinkage void
@@ -1627,9 +1606,8 @@ syscall_trace_enter (long arg0, long arg
long arg4, long arg5, long arg6, long arg7,
struct pt_regs regs)
{
- if (test_thread_flag(TIF_SYSCALL_TRACE)
- && (current->ptrace & PT_PTRACED))
- syscall_trace();
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(®s, 0);
if (unlikely(current->audit_context)) {
long syscall;
@@ -1664,8 +1642,11 @@ syscall_trace_leave (long arg0, long arg
audit_syscall_exit(success, result);
}
- if ((test_thread_flag(TIF_SYSCALL_TRACE)
- || test_thread_flag(TIF_SINGLESTEP))
- && (current->ptrace & PT_PTRACED))
- syscall_trace();
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall(®s, 1);
+
+ if (test_thread_flag(TIF_SINGLESTEP)) {
+ force_sig(SIGTRAP, current); /* XXX */
+ tracehook_report_syscall_step(®s);
+ }
}
--- linux-2.6/arch/ia64/kernel/signal.c
+++ linux-2.6/arch/ia64/kernel/signal.c
@@ -10,7 +10,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/smp.h>
@@ -429,6 +429,8 @@ handle_signal (unsigned long sig, struct
sigaddset(¤t->blocked, sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
+
+ tracehook_report_handle_signal(sig, ka, oldset, &scr->pt);
return 1;
}
--- linux-2.6/include/asm-ia64/tracehook.h
+++ linux-2.6/include/asm-ia64/tracehook.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C)2006 Intel Co
+ * Anil S Keshavamurthy <anil.s.keshavamurthy at intel.com>
+ * and Bibo Mao <bibo.mao at intel.com> adapted from i386.
+ *
+ * Tracing hooks, ia64 CPU support
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+#define ARCH_HAS_BLOCK_STEP (1)
+
+static inline void tracehook_enable_single_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->ss = 1;
+ set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_disable_single_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->ss = 0;
+ if (ia64_psr(pt)->tb == 0)
+ clear_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_enable_block_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->tb = 1;
+ set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_disable_block_step(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ ia64_psr(pt)->tb = 0;
+ if (ia64_psr(pt)->ss == 0)
+ clear_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+ struct pt_regs *pt = task_pt_regs(tsk);
+ return ia64_psr(pt)->ss;
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->r15 = -1L;
+}
+
+#endif
linux-2.6-utrace-tracehook-s390.patch:
arch/s390/kernel/compat_signal.c | 5 ++-
arch/s390/kernel/ptrace.c | 62 +++++++++++++++++++++------------------
arch/s390/kernel/signal.c | 3 +
arch/s390/kernel/traps.c | 6 +--
include/asm-s390/tracehook.h | 46 ++++++++++++++++++++++++++++
5 files changed, 90 insertions(+), 32 deletions(-)
--- NEW FILE linux-2.6-utrace-tracehook-s390.patch ---
[PATCH 1c] utrace: tracehook for s390
This patch does the initial tracehook conversion for s390.
Signed-off-by: Roland McGrath <roland at redhat.com>
Signed-off-by: David Wilder <dwilder at us.ibm.com>
---
arch/s390/kernel/traps.c | 6 ++--
arch/s390/kernel/compat_signal.c | 5 ++-
arch/s390/kernel/ptrace.c | 62 +++++++++++++++++++++-----------------
arch/s390/kernel/signal.c | 3 ++
include/asm-s390/tracehook.h | 46 ++++++++++++++++++++++++++++
5 files changed, 90 insertions(+), 32 deletions(-)
create include/asm-s390/tracehook.h
--- linux-2.6/arch/s390/kernel/traps.c
+++ linux-2.6/arch/s390/kernel/traps.c
@@ -18,7 +18,7 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/smp.h>
@@ -338,7 +338,7 @@ void __kprobes do_single_step(struct pt_
SIGTRAP) == NOTIFY_STOP){
return;
}
- if ((current->ptrace & PT_PTRACED) != 0)
+ if (tracehook_consider_fatal_signal(current, SIGTRAP))
force_sig(SIGTRAP, current);
}
@@ -439,7 +439,7 @@ static void illegal_op(struct pt_regs *
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
return;
if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
- if (current->ptrace & PT_PTRACED)
+ if (tracehook_consider_fatal_signal(current, SIGTRAP))
force_sig(SIGTRAP, current);
else
signal = SIGILL;
--- linux-2.6/arch/s390/kernel/compat_signal.c
+++ linux-2.6/arch/s390/kernel/compat_signal.c
@@ -27,6 +27,7 @@
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/lowcore.h>
+#include <linux/tracehook.h>
#include "compat_linux.h"
#include "compat_ptrace.h"
@@ -580,7 +581,9 @@ handle_signal32(unsigned long sig, struc
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
+
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
}
+
return ret;
}
-
--- linux-2.6/arch/s390/kernel/ptrace.c
+++ linux-2.6/arch/s390/kernel/ptrace.c
@@ -29,6 +29,7 @@
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
@@ -84,18 +85,35 @@ FixPerRegisters(struct task_struct *task
per_info->control_regs.bits.storage_alt_space_ctl = 1;
else
per_info->control_regs.bits.storage_alt_space_ctl = 0;
+
+ if (task == current)
+ /*
+ * These registers are loaded in __switch_to on
+ * context switch. We must load them now if
+ * touching the current thread.
+ */
+ __ctl_load(per_info->control_regs.words.cr, 9, 11);
}
-static void set_single_step(struct task_struct *task)
+void
+tracehook_enable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 1;
FixPerRegisters(task);
}
-static void clear_single_step(struct task_struct *task)
+void
+tracehook_disable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 0;
FixPerRegisters(task);
+ clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
+}
+
+int
+tracehook_single_step_enabled(struct task_struct *task)
+{
+ return task->thread.per_info.single_step;
}
/*
@@ -107,7 +125,7 @@ void
ptrace_disable(struct task_struct *child)
{
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ tracehook_disable_single_step(child);
}
#ifndef CONFIG_64BIT
@@ -593,6 +611,7 @@ do_ptrace_emu31(struct task_struct *chil
copied += sizeof(unsigned int);
}
return 0;
+#if 0 /* XXX */
case PTRACE_GETEVENTMSG:
return put_user((__u32) child->ptrace_message,
(unsigned int __force __user *) data);
@@ -658,7 +677,7 @@ do_ptrace(struct task_struct *child, lon
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ tracehook_disable_single_step(child);
wake_up_process(child);
return 0;
@@ -672,7 +691,7 @@ do_ptrace(struct task_struct *child, lon
return 0;
child->exit_code = SIGKILL;
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ tracehook_disable_single_step(child);
wake_up_process(child);
return 0;
@@ -685,7 +704,7 @@ do_ptrace(struct task_struct *child, lon
if (data)
set_tsk_thread_flag(child, TIF_SINGLE_STEP);
else
- set_single_step(child);
+ tracehook_enable_single_step(child);
/* give it a chance to run. */
wake_up_process(child);
return 0;
@@ -738,30 +757,17 @@ syscall_trace(struct pt_regs *regs, int
if (unlikely(current->audit_context) && entryexit)
audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- goto out;
- if (!(current->ptrace & PT_PTRACED))
- goto out;
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
-
- /*
- * If the debuffer has set an invalid system call number,
- * we prepare to skip the system call restart handling.
- */
- if (!entryexit && regs->gprs[2] >= NR_syscalls)
- regs->trap = -1;
+ if (test_thread_flag(TIF_SYSCALL_TRACE)) {
+ tracehook_report_syscall(regs, entryexit);
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ /*
+ * If the debugger has set an invalid system call number,
+ * we prepare to skip the system call restart handling.
+ */
+ if (!entryexit && regs->gprs[2] >= NR_syscalls)
+ regs->trap = -1;
}
- out:
+
if (unlikely(current->audit_context) && !entryexit)
audit_syscall_entry(test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
--- linux-2.6/arch/s390/kernel/signal.c
+++ linux-2.6/arch/s390/kernel/signal.c
@@ -24,6 +24,7 @@
#include <linux/tty.h>
#include <linux/personality.h>
#include <linux/binfmts.h>
+#include <linux/tracehook.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/lowcore.h>
@@ -396,6 +397,8 @@ handle_signal(unsigned long sig, struct
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
+
+ tracehook_report_handle_signal(sig, ka, oldset, regs);
}
return ret;
--- linux-2.6/include/asm-s390/tracehook.h
+++ linux-2.6/include/asm-s390/tracehook.h
@@ -0,0 +1,46 @@
+/*
+ * Tracing hooks, s390/s390x support.
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * Red Hat Author: Roland McGrath.
+ */
+
+#ifndef _ASM_TRACEHOOK_H
+#define _ASM_TRACEHOOK_H 1
+
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/*
+ * See linux/tracehook.h for the descriptions of what these need to do.
+ */
+
+#define ARCH_HAS_SINGLE_STEP (1)
+
+/* These three are defined in arch/s390/kernel/ptrace.c. */
+void tracehook_enable_single_step(struct task_struct *tsk);
+void tracehook_disable_single_step(struct task_struct *tsk);
+int tracehook_single_step_enabled(struct task_struct *tsk);
+
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+ regs->gprs[2] = -1L;
+}
+
+#endif
linux-2.6-utrace-tracehook-sparc64.patch:
arch/sparc64/kernel/entry.S | 6 ------
arch/sparc64/kernel/ptrace.c | 29 +++++++++--------------------
arch/sparc64/kernel/signal.c | 2 ++
arch/sparc64/kernel/signal32.c | 2 ++
include/asm-sparc64/tracehook.h | 40 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 53 insertions(+), 26 deletions(-)
Index: linux-2.6-utrace-tracehook-sparc64.patch
===================================================================
RCS file: linux-2.6-utrace-tracehook-sparc64.patch
diff -N linux-2.6-utrace-tracehook-sparc64.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-tracehook-sparc64.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,196 @@
+[PATCH 1b] utrace: tracehook for sparc64
+
+This patch does the initial tracehook conversion for sparc64.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+
+---
+
+ arch/sparc64/kernel/ptrace.c | 29 +++++++++-------------------
+ arch/sparc64/kernel/signal.c | 2 ++
+ arch/sparc64/kernel/signal32.c | 2 ++
+ arch/sparc64/kernel/entry.S | 6 ------
+ include/asm-sparc64/tracehook.h | 40 +++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 53 insertions(+), 26 deletions(-)
+ create include/asm-sparc64/tracehook.h
+
+--- linux-2.6/arch/sparc64/kernel/ptrace.c
++++ linux-2.6/arch/sparc64/kernel/ptrace.c
+@@ -22,6 +22,7 @@
+ #include <linux/seccomp.h>
+ #include <linux/audit.h>
+ #include <linux/signal.h>
++#include <linux/tracehook.h>
+
+ #include <asm/asi.h>
+ #include <asm/pgtable.h>
+@@ -33,6 +34,7 @@
+ #include <asm/page.h>
+ #include <asm/cpudata.h>
+
++#if 0 /* XXX */
+ /* Returning from ptrace is a bit tricky because the syscall return
+ * low level code assumes any value returned which is negative and
+ * is a valid errno will mean setting the condition codes to indicate
+@@ -82,6 +84,7 @@ pt_os_succ_return (struct pt_regs *regs,
+ else
+ pt_succ_return_linux (regs, val, addr);
+ }
++#endif
+
+ /* #define ALLOW_INIT_TRACING */
+ /* #define DEBUG_PTRACE */
+@@ -167,6 +170,7 @@ void flush_ptrace_access(struct vm_area_
+ }
+ }
+
++#if 0 /* XXX */
+ asmlinkage void do_ptrace(struct pt_regs *regs)
+ {
+ int request = regs->u_regs[UREG_I0];
+@@ -643,11 +647,13 @@ out_tsk:
+ out:
+ unlock_kernel();
+ }
++#endif
+
+ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
+ {
+ /* do the secure computing check first */
+- secure_computing(regs->u_regs[UREG_G1]);
++ if (!syscall_exit_p)
++ secure_computing(regs->u_regs[UREG_G1]);
+
+ if (unlikely(current->audit_context) && syscall_exit_p) {
+ unsigned long tstate = regs->tstate;
+@@ -659,26 +665,9 @@ asmlinkage void syscall_trace(struct pt_
+ audit_syscall_exit(result, regs->u_regs[UREG_I0]);
+ }
+
+- if (!(current->ptrace & PT_PTRACED))
+- goto out;
+-
+- if (!test_thread_flag(TIF_SYSCALL_TRACE))
+- goto out;
+-
+- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+- ? 0x80 : 0));
++ if (test_thread_flag(TIF_SYSCALL_TRACE))
++ tracehook_report_syscall(regs, syscall_exit_p);
+
+- /*
+- * this isn't the same as continuing with a signal, but it will do
+- * for normal use. strace only continues with a signal if the
+- * stopping signal is not SIGTRAP. -brl
+- */
+- if (current->exit_code) {
+- send_sig(current->exit_code, current, 1);
+- current->exit_code = 0;
+- }
+-
+-out:
+ if (unlikely(current->audit_context) && !syscall_exit_p)
+ audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
+ AUDIT_ARCH_SPARC :
+--- linux-2.6/arch/sparc64/kernel/signal.c
++++ linux-2.6/arch/sparc64/kernel/signal.c
+@@ -22,6 +22,7 @@
+ #include <linux/tty.h>
+ #include <linux/binfmts.h>
+ #include <linux/bitops.h>
++#include <linux/tracehook.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/ptrace.h>
+@@ -490,6 +491,7 @@ static inline void handle_signal(unsigne
+ sigaddset(¤t->blocked,signr);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
++ tracehook_report_handle_signal(signr, ka, oldset, regs);
+ }
+
+ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
+--- linux-2.6/arch/sparc64/kernel/signal32.c
++++ linux-2.6/arch/sparc64/kernel/signal32.c
+@@ -20,6 +20,7 @@
+ #include <linux/binfmts.h>
+ #include <linux/compat.h>
+ #include <linux/bitops.h>
++#include <linux/tracehook.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/ptrace.h>
+@@ -1236,6 +1237,7 @@ static inline void handle_signal32(unsig
+ sigaddset(¤t->blocked,signr);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
++ tracehook_report_handle_signal(signr, ka, oldset, regs);
+ }
+
+ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs,
+--- linux-2.6/arch/sparc64/kernel/entry.S
++++ linux-2.6/arch/sparc64/kernel/entry.S
+@@ -1434,7 +1434,6 @@ execve_merge:
+
+ .globl sys_pipe, sys_sigpause, sys_nis_syscall
+ .globl sys_rt_sigreturn
+- .globl sys_ptrace
+ .globl sys_sigaltstack
+ .align 32
+ sys_pipe: ba,pt %xcc, sparc_pipe
+@@ -1477,11 +1476,6 @@ sys32_rt_sigreturn:
+ add %o7, 1f-.-4, %o7
+ nop
+ #endif
+-sys_ptrace: add %sp, PTREGS_OFF, %o0
+- call do_ptrace
+- add %o7, 1f-.-4, %o7
+- nop
+- .align 32
+ 1: ldx [%curptr + TI_FLAGS], %l5
+ andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0
+ be,pt %icc, rtrap
+--- linux-2.6/include/asm-sparc64/tracehook.h
++++ linux-2.6/include/asm-sparc64/tracehook.h
+@@ -0,0 +1,40 @@
++/*
++ * Tracing hooks, SPARC64 CPU support
++ *
++ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ */
++
++#ifndef _ASM_TRACEHOOK_H
++#define _ASM_TRACEHOOK_H 1
++
++
++#include <linux/sched.h>
++#include <asm/ptrace.h>
++
++/*
++ * See linux/tracehook.h for the descriptions of what these need to do.
++ */
++
++
++static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
++{
++ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
++{
++ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_abort_syscall(struct pt_regs *regs)
++{
++ regs->u_regs[UREG_G1] = -1L;
++}
++
++#endif
linux-2.6-utrace-tracehook-um.patch:
arch/um/kernel/exec.c | 1
arch/um/kernel/process.c | 6
arch/um/kernel/ptrace.c | 327 ++++------------------------------------
arch/um/kernel/signal.c | 5
arch/um/kernel/skas/syscall.c | 4
arch/um/sys-i386/signal.c | 4
include/asm-um/ptrace-generic.h | 3
include/asm-um/ptrace-i386.h | 2
include/asm-um/ptrace-x86_64.h | 2
include/asm-um/thread_info.h | 3
include/asm-um/tracehook.h | 57 ++++++
11 files changed, 106 insertions(+), 308 deletions(-)
Index: linux-2.6-utrace-tracehook-um.patch
===================================================================
RCS file: linux-2.6-utrace-tracehook-um.patch
diff -N linux-2.6-utrace-tracehook-um.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-tracehook-um.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,596 @@
+[PATCH 1d] utrace: tracehook for UML
+
+From: Jeff Dike <jdike at addtoit.com>
+
+This is the tracehook part of the UML utrace work, enough to get UML
+building with the utrace prep patches applied.
+
+Checks of task->ptrace & PT_DTRACE were replaced with
+test_thread_flag(TIF_SINGLESTEP, or removed, in the case of execve.
+
+Most of arch/um/kernel/ptrace.c is gone, to be reinstated in future
+utrace work.
+
+Similarly, calls to syscall_trace and ptrace notifications in the
+signal delivery code are gone.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+Signed-off-by: Jeff Dike <jdike at linux.intel.com>
+
+---
+
+ arch/um/kernel/ptrace.c | 327 ++++-----------------------------------
+ arch/um/kernel/skas/syscall.c | 4
+ arch/um/kernel/signal.c | 5 -
+ arch/um/kernel/exec.c | 1
+ arch/um/kernel/process.c | 6 -
+ arch/um/sys-i386/signal.c | 4
+ include/asm-um/ptrace-x86_64.h | 2
+ include/asm-um/ptrace-generic.h | 3
+ include/asm-um/ptrace-i386.h | 2
+ include/asm-um/tracehook.h | 57 +++++++
+ include/asm-um/thread_info.h | 3
+ 11 files changed, 106 insertions(+), 308 deletions(-)
+ create include/asm-um/tracehook.h
+
+--- linux-2.6/arch/um/kernel/ptrace.c
++++ linux-2.6/arch/um/kernel/ptrace.c
+@@ -3,261 +3,21 @@
+ * Licensed under the GPL
+ */
+
+-#include "linux/sched.h"
+-#include "linux/mm.h"
+-#include "linux/errno.h"
+-#include "linux/smp_lock.h"
+-#include "linux/security.h"
+-#include "linux/ptrace.h"
+-#include "linux/audit.h"
+-#ifdef CONFIG_PROC_MM
+-#include "linux/proc_mm.h"
+-#endif
+-#include "asm/ptrace.h"
+-#include "asm/uaccess.h"
+-#include "kern_util.h"
+-#include "skas_ptrace.h"
+-#include "sysdep/ptrace.h"
+-#include "os.h"
+-
+-static inline void set_singlestepping(struct task_struct *child, int on)
+-{
+- if (on)
+- child->ptrace |= PT_DTRACE;
+- else
+- child->ptrace &= ~PT_DTRACE;
+- child->thread.singlestep_syscall = 0;
+-
+-#ifdef SUBARCH_SET_SINGLESTEPPING
+- SUBARCH_SET_SINGLESTEPPING(child, on);
+-#endif
+-}
++#include <linux/audit.h>
++#include <linux/elf.h>
++#include <linux/module.h>
++#include <linux/ptrace.h>
++#include <linux/tracehook.h>
+
+ /*
+ * Called by kernel/ptrace.c when detaching..
+ */
+ void ptrace_disable(struct task_struct *child)
+ {
+- set_singlestepping(child,0);
+-}
+-
+-extern int peek_user(struct task_struct * child, long addr, long data);
+-extern int poke_user(struct task_struct * child, long addr, long data);
+-
+-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+-{
+- int i, ret;
+- unsigned long __user *p = (void __user *)(unsigned long)data;
+-
+- switch (request) {
+- /* when I and D space are separate, these will need to be fixed. */
+- case PTRACE_PEEKTEXT: /* read word at location addr. */
+- case PTRACE_PEEKDATA: {
+- unsigned long tmp;
+- int copied;
+-
+- ret = -EIO;
+- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+- if (copied != sizeof(tmp))
+- break;
+- ret = put_user(tmp, p);
+- break;
+- }
+-
+- /* read the word at location addr in the USER area. */
+- case PTRACE_PEEKUSR:
+- ret = peek_user(child, addr, data);
+- break;
+-
+- /* when I and D space are separate, this will have to be fixed. */
+- case PTRACE_POKETEXT: /* write the word at location addr. */
+- case PTRACE_POKEDATA:
+- ret = -EIO;
+- if (access_process_vm(child, addr, &data, sizeof(data),
+- 1) != sizeof(data))
+- break;
+- ret = 0;
+- break;
+-
+- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+- ret = poke_user(child, addr, data);
+- break;
+-
+- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+- case PTRACE_CONT: { /* restart after signal. */
+- ret = -EIO;
+- if (!valid_signal(data))
+- break;
+-
+- set_singlestepping(child, 0);
+- if (request == PTRACE_SYSCALL) {
+- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+- }
+- else {
+- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+- }
+- child->exit_code = data;
+- wake_up_process(child);
+- ret = 0;
+- break;
+- }
+-
+-/*
+- * make the child exit. Best I can do is send it a sigkill.
+- * perhaps it should be put in the status that it wants to
+- * exit.
+- */
+- case PTRACE_KILL: {
+- ret = 0;
+- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
+- break;
+-
+- set_singlestepping(child, 0);
+- child->exit_code = SIGKILL;
+- wake_up_process(child);
+- break;
+- }
+-
+- case PTRACE_SINGLESTEP: { /* set the trap flag. */
+- ret = -EIO;
+- if (!valid_signal(data))
+- break;
+- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+- set_singlestepping(child, 1);
+- child->exit_code = data;
+- /* give it a chance to run. */
+- wake_up_process(child);
+- ret = 0;
+- break;
+- }
+-
+- case PTRACE_DETACH:
+- /* detach a process that was attached. */
+- ret = ptrace_detach(child, data);
+- break;
+-
+-#ifdef PTRACE_GETREGS
+- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
+- if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) {
+- ret = -EIO;
+- break;
+- }
+- for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
+- __put_user(getreg(child, i), p);
+- p++;
+- }
+- ret = 0;
+- break;
+- }
+-#endif
+-#ifdef PTRACE_SETREGS
+- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
+- unsigned long tmp = 0;
+- if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) {
+- ret = -EIO;
+- break;
+- }
+- for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
+- __get_user(tmp, p);
+- putreg(child, i, tmp);
+- p++;
+- }
+- ret = 0;
+- break;
+- }
+-#endif
+-#ifdef PTRACE_GETFPREGS
+- case PTRACE_GETFPREGS: /* Get the child FPU state. */
+- ret = get_fpregs(data, child);
+- break;
+-#endif
+-#ifdef PTRACE_SETFPREGS
+- case PTRACE_SETFPREGS: /* Set the child FPU state. */
+- ret = set_fpregs(data, child);
+- break;
+-#endif
+-#ifdef PTRACE_GETFPXREGS
+- case PTRACE_GETFPXREGS: /* Get the child FPU state. */
+- ret = get_fpxregs(data, child);
+- break;
+-#endif
+-#ifdef PTRACE_SETFPXREGS
+- case PTRACE_SETFPXREGS: /* Set the child FPU state. */
+- ret = set_fpxregs(data, child);
+- break;
+-#endif
+- case PTRACE_GET_THREAD_AREA:
+- ret = ptrace_get_thread_area(child, addr,
+- (struct user_desc __user *) data);
+- break;
+-
+- case PTRACE_SET_THREAD_AREA:
+- ret = ptrace_set_thread_area(child, addr,
+- (struct user_desc __user *) data);
+- break;
+-
+- case PTRACE_FAULTINFO: {
+- /* Take the info from thread->arch->faultinfo,
+- * but transfer max. sizeof(struct ptrace_faultinfo).
+- * On i386, ptrace_faultinfo is smaller!
+- */
+- ret = copy_to_user(p, &child->thread.arch.faultinfo,
+- sizeof(struct ptrace_faultinfo));
+- if(ret)
+- break;
+- break;
+- }
+-
+-#ifdef PTRACE_LDT
+- case PTRACE_LDT: {
+- struct ptrace_ldt ldt;
+-
+- if(copy_from_user(&ldt, p, sizeof(ldt))){
+- ret = -EIO;
+- break;
+- }
+-
+- /* This one is confusing, so just punt and return -EIO for
+- * now
+- */
+- ret = -EIO;
+- break;
+- }
+-#endif
+-#ifdef CONFIG_PROC_MM
+- case PTRACE_SWITCH_MM: {
+- struct mm_struct *old = child->mm;
+- struct mm_struct *new = proc_mm_get_mm(data);
+-
+- if(IS_ERR(new)){
+- ret = PTR_ERR(new);
+- break;
+- }
+-
+- atomic_inc(&new->mm_users);
+- child->mm = new;
+- child->active_mm = new;
+- mmput(old);
+- ret = 0;
+- break;
+- }
+-#endif
+-#ifdef PTRACE_ARCH_PRCTL
+- case PTRACE_ARCH_PRCTL:
+- /* XXX Calls ptrace on the host - needs some SMP thinking */
+- ret = arch_prctl_skas(child, data, (void *) addr);
+- break;
+-#endif
+- default:
+- ret = ptrace_request(child, request, addr, data);
+- break;
+- }
+-
+- return ret;
+ }
+
+-void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
+- int error_code)
++static void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
++ int error_code)
+ {
+ struct siginfo info;
+
+@@ -266,56 +26,39 @@ void send_sigtrap(struct task_struct *ts
+ info.si_code = TRAP_BRKPT;
+
+ /* User-mode eip? */
+- info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
++ info.si_addr = UPT_IS_USER(®s->regs) ?
++ (void __user *) UPT_IP(®s->regs) : NULL;
+
+ /* Send us the fakey SIGTRAP */
+ force_sig_info(SIGTRAP, &info, tsk);
+ }
+
+-/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
+- * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
++/* notification of system call entry/exit
++ * - triggered by current->work.syscall_trace
+ */
+-void syscall_trace(union uml_pt_regs *regs, int entryexit)
++void do_syscall_trace(struct pt_regs *regs, int entryexit)
+ {
+- int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
+- int tracesysgood;
+-
+- if (unlikely(current->audit_context)) {
+- if (!entryexit)
+- audit_syscall_entry(HOST_AUDIT_ARCH,
+- UPT_SYSCALL_NR(regs),
+- UPT_SYSCALL_ARG1(regs),
+- UPT_SYSCALL_ARG2(regs),
+- UPT_SYSCALL_ARG3(regs),
+- UPT_SYSCALL_ARG4(regs));
+- else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
+- UPT_SYSCALL_RET(regs));
+- }
+-
+- /* Fake a debug trap */
+- if (is_singlestep)
+- send_sigtrap(current, regs, 0);
+-
+- if (!test_thread_flag(TIF_SYSCALL_TRACE))
+- return;
+-
+- if (!(current->ptrace & PT_PTRACED))
+- return;
+-
+- /* the 0x80 provides a way for the tracing parent to distinguish
+- between a syscall stop and SIGTRAP delivery */
+- tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
+- ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
+-
+- if (entryexit) /* force do_signal() --> is_syscall() */
+- set_thread_flag(TIF_SIGPENDING);
+-
+- /* this isn't the same as continuing with a signal, but it will do
+- * for normal use. strace only continues with a signal if the
+- * stopping signal is not SIGTRAP. -brl
+- */
+- if (current->exit_code) {
+- send_sig(current->exit_code, current, 1);
+- current->exit_code = 0;
+- }
++ /* do the secure computing check first */
++ if (!entryexit)
++ secure_computing(PT_REGS_SYSCALL_NR(regs));
++
++ if (unlikely(current->audit_context) && entryexit)
++ audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(®s->regs)),
++ UPT_SYSCALL_RET(®s->regs));
++
++ if (test_thread_flag(TIF_SYSCALL_TRACE))
++ tracehook_report_syscall(regs, entryexit);
++
++ if (test_thread_flag(TIF_SINGLESTEP) && entryexit) {
++ send_sigtrap(current, regs, 0); /* XXX */
++ tracehook_report_syscall_step(regs);
++ }
++
++ if (unlikely(current->audit_context) && !entryexit)
++ audit_syscall_entry(HOST_AUDIT_ARCH,
++ UPT_SYSCALL_NR(®s->regs),
++ UPT_SYSCALL_ARG1(®s->regs),
++ UPT_SYSCALL_ARG2(®s->regs),
++ UPT_SYSCALL_ARG3(®s->regs),
++ UPT_SYSCALL_ARG4(®s->regs));
+ }
+--- linux-2.6/arch/um/kernel/skas/syscall.c
++++ linux-2.6/arch/um/kernel/skas/syscall.c
+@@ -19,8 +19,6 @@ void handle_syscall(union uml_pt_regs *r
+ long result;
+ int syscall;
+
+- syscall_trace(r, 0);
+-
+ current->thread.nsyscalls++;
+ nsyscalls++;
+
+@@ -38,6 +36,4 @@ void handle_syscall(union uml_pt_regs *r
+ else result = EXECUTE_SYSCALL(syscall, regs);
+
+ REGS_SET_SYSCALL_RETURN(r->skas.regs, result);
+-
+- syscall_trace(r, 1);
+ }
+--- linux-2.6/arch/um/kernel/signal.c
++++ linux-2.6/arch/um/kernel/signal.c
+@@ -14,6 +14,7 @@
+ #include "linux/tty.h"
+ #include "linux/binfmts.h"
+ #include "linux/ptrace.h"
++#include "linux/tracehook.h"
+ #include "asm/signal.h"
+ #include "asm/uaccess.h"
+ #include "asm/unistd.h"
+@@ -92,6 +93,8 @@ static int handle_signal(struct pt_regs
+ sigaddset(¤t->blocked, signr);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
++
++ tracehook_report_handle_signal(signr, ka, oldset, regs);
+ }
+
+ return err;
+@@ -147,7 +150,7 @@ static int kern_do_signal(struct pt_regs
+ * on the host. The tracing thread will check this flag and
+ * PTRACE_SYSCALL if necessary.
+ */
+- if(current->ptrace & PT_DTRACE)
++ if(test_thread_flag(TIF_SYSCALL_TRACE))
+ current->thread.singlestep_syscall =
+ is_syscall(PT_REGS_IP(¤t->thread.regs));
+
+--- linux-2.6/arch/um/kernel/exec.c
++++ linux-2.6/arch/um/kernel/exec.c
+@@ -51,7 +51,6 @@ static long execve1(char *file, char __u
+ error = do_execve(file, argv, env, ¤t->thread.regs);
+ if (error == 0){
+ task_lock(current);
+- current->ptrace &= ~PT_DTRACE;
+ #ifdef SUBARCH_EXECVE1
+ SUBARCH_EXECVE1(¤t->thread.regs.regs);
+ #endif
+--- linux-2.6/arch/um/kernel/process.c
++++ linux-2.6/arch/um/kernel/process.c
+@@ -403,11 +403,11 @@ int singlestepping(void * t)
+ {
+ struct task_struct *task = t ? t : current;
+
+- if ( ! (task->ptrace & PT_DTRACE) )
+- return(0);
++ if (!test_thread_flag(TIF_SINGLESTEP))
++ return 0;
+
+ if (task->thread.singlestep_syscall)
+- return(1);
++ return 1;
+
+ return 2;
+ }
+--- linux-2.6/arch/um/sys-i386/signal.c
++++ linux-2.6/arch/um/sys-i386/signal.c
+@@ -274,8 +274,6 @@ int setup_signal_stack_sc(unsigned long
+ PT_REGS_EDX(regs) = (unsigned long) 0;
+ PT_REGS_ECX(regs) = (unsigned long) 0;
+
+- if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+- ptrace_notify(SIGTRAP);
+ return 0;
+
+ err:
+@@ -331,8 +329,6 @@ int setup_signal_stack_si(unsigned long
+ PT_REGS_EDX(regs) = (unsigned long) &frame->info;
+ PT_REGS_ECX(regs) = (unsigned long) &frame->uc;
+
+- if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+- ptrace_notify(SIGTRAP);
+ return 0;
+
+ err:
+--- linux-2.6/include/asm-um/ptrace-x86_64.h
++++ linux-2.6/include/asm-um/ptrace-x86_64.h
+@@ -14,6 +14,8 @@
+ #define __FRAME_OFFSETS /* Needed to get the R* macros */
+ #include "asm/ptrace-generic.h"
+
++#define ARCH_HAS_SINGLE_STEP (1)
++
+ #define HOST_AUDIT_ARCH AUDIT_ARCH_X86_64
+
+ /* Also defined in sysdep/ptrace.h, so may already be defined. */
+--- linux-2.6/include/asm-um/ptrace-generic.h
++++ linux-2.6/include/asm-um/ptrace-generic.h
+@@ -44,9 +44,6 @@ extern int set_fpxregs(unsigned long buf
+
+ extern void show_regs(struct pt_regs *regs);
+
+-extern void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
+- int error_code);
+-
+ extern int arch_copy_tls(struct task_struct *new);
+ extern void clear_flushed_tls(struct task_struct *task);
+
+--- linux-2.6/include/asm-um/ptrace-i386.h
++++ linux-2.6/include/asm-um/ptrace-i386.h
+@@ -6,6 +6,8 @@
+ #ifndef __UM_PTRACE_I386_H
+ #define __UM_PTRACE_I386_H
+
++#define ARCH_HAS_SINGLE_STEP (1)
++
+ #define HOST_AUDIT_ARCH AUDIT_ARCH_I386
+
+ #include "linux/compiler.h"
+--- linux-2.6/include/asm-um/tracehook.h
++++ linux-2.6/include/asm-um/tracehook.h
+@@ -0,0 +1,57 @@
++/*
++ * Tracing hooks, i386 CPU support
++ *
++ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ *
++ * Munged for UML - jdike@{addtoit,linux.intel}.com
++ */
++
++#ifndef _ASM_TRACEHOOK_H
++#define _ASM_TRACEHOOK_H 1
++
++#include <linux/sched.h>
++#include <asm/ptrace.h>
++#include <asm/thread_info.h>
++
++/*
++ * See linux/tracehook.h for the descriptions of what these need to do.
++ */
++
++static inline void tracehook_enable_single_step(struct task_struct *tsk)
++{
++ set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
++}
++
++static inline void tracehook_disable_single_step(struct task_struct *tsk)
++{
++ clear_tsk_thread_flag(tsk, TIF_SINGLESTEP);
++}
++
++static inline int tracehook_single_step_enabled(struct task_struct *tsk)
++{
++ return test_tsk_thread_flag(tsk, TIF_SINGLESTEP);
++}
++
++static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
++{
++ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
++{
++ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_abort_syscall(struct pt_regs *regs)
++{
++ PT_REGS_SYSCALL_NR(regs) = -1;
++}
++
++
++#endif
+--- linux-2.6/include/asm-um/thread_info.h
++++ linux-2.6/include/asm-um/thread_info.h
+@@ -71,6 +71,8 @@ static inline struct thread_info *curren
+ #define TIF_MEMDIE 5
+ #define TIF_SYSCALL_AUDIT 6
+ #define TIF_RESTORE_SIGMASK 7
++#define TIF_SINGLESTEP 8 /* restore singlestep on return to user
++ * mode */
+
+ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
+ #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
+@@ -79,5 +81,6 @@ static inline struct thread_info *curren
+ #define _TIF_MEMDIE (1 << TIF_MEMDIE)
+ #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
+ #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
++#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
+
+ #endif
linux-2.6-utrace-tracehook.patch:
arch/alpha/kernel/asm-offsets.c | 2
arch/alpha/kernel/entry.S | 4
arch/arm/kernel/ptrace.c | 17 -
arch/arm26/kernel/ptrace.c | 32 --
arch/frv/kernel/ptrace.c | 15 -
arch/i386/kernel/entry.S | 7
arch/i386/kernel/process.c | 3
arch/i386/kernel/ptrace.c | 104 +--------
arch/i386/kernel/signal.c | 37 +--
arch/i386/kernel/vm86.c | 7
arch/i386/math-emu/fpu_entry.c | 6
arch/ia64/kernel/asm-offsets.c | 2
arch/ia64/kernel/fsys.S | 16 -
arch/ia64/kernel/mca.c | 2
arch/mips/kernel/ptrace.c | 21 -
arch/mips/kernel/sysirix.c | 2
arch/powerpc/kernel/asm-offsets.c | 2
arch/powerpc/kernel/process.c | 5
arch/powerpc/kernel/ptrace-common.h | 16 -
arch/powerpc/kernel/ptrace.c | 76 +-----
arch/powerpc/kernel/ptrace32.c | 13 -
arch/powerpc/kernel/signal_32.c | 3
arch/powerpc/kernel/signal_64.c | 3
arch/powerpc/kernel/sys_ppc32.c | 5
arch/ppc/kernel/asm-offsets.c | 2
arch/s390/kernel/compat_linux.c | 3
arch/s390/kernel/process.c | 3
arch/sparc64/kernel/binfmt_aout32.c | 2
arch/sparc64/kernel/process.c | 3
arch/sparc64/kernel/sys_sparc32.c | 3
arch/x86_64/ia32/ia32_aout.c | 6
arch/x86_64/ia32/ia32_signal.c | 7
arch/x86_64/ia32/ia32entry.S | 4
arch/x86_64/ia32/ptrace32.c | 2
arch/x86_64/ia32/sys_ia32.c | 5
arch/x86_64/kernel/entry.S | 8
arch/x86_64/kernel/process.c | 5
arch/x86_64/kernel/ptrace.c | 57 +---
arch/x86_64/kernel/signal.c | 28 +-
arch/x86_64/kernel/traps.c | 8
arch/x86_64/mm/fault.c | 4
drivers/connector/cn_proc.c | 4
fs/binfmt_aout.c | 6
fs/binfmt_elf.c | 6
fs/binfmt_elf_fdpic.c | 7
fs/binfmt_flat.c | 3
fs/binfmt_som.c | 2
fs/exec.c | 11
fs/proc/array.c | 12 -
fs/proc/base.c | 17 -
include/asm-i386/signal.h | 4
include/asm-i386/thread_info.h | 7
include/asm-i386/tracehook.h | 52 ++++
include/asm-powerpc/tracehook.h | 74 ++++++
include/asm-x86_64/thread_info.h | 3
include/asm-x86_64/tracehook.h | 51 ++++
include/linux/init_task.h | 3
include/linux/ptrace.h | 18 -
include/linux/sched.h | 16 -
include/linux/tracehook.h | 414 ++++++++++++++++++++++++++++++++++++
kernel/exit.c | 244 +++++----------------
kernel/fork.c | 66 +----
kernel/ptrace.c | 299 +-------------------------
kernel/signal.c | 212 +++---------------
kernel/sys.c | 2
kernel/timer.c | 6
kernel/tsacct.c | 2
mm/nommu.c | 4
security/selinux/hooks.c | 54 ++--
security/selinux/include/objsec.h | 1
70 files changed, 934 insertions(+), 1216 deletions(-)
View full diff with command:
/usr/bin/cvs -f diff -kk -u -N -r 1.2 -r 1.3 linux-2.6-utrace-tracehook.patch
Index: linux-2.6-utrace-tracehook.patch
===================================================================
RCS file: linux-2.6-utrace-tracehook.patch
diff -N linux-2.6-utrace-tracehook.patch
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-2.6-utrace-tracehook.patch 19 Jul 2007 17:46:36 -0000 1.3
@@ -0,0 +1,4000 @@
+[PATCH 1] utrace: tracehook (die, ptrace, die)
+
+This patch rips out the old ptrace implementation. It leaves behind stubs
+so ptrace just fails with ENOSYS. This is not really a useful kernel tree
+to build, but it's a clean removal of the old code before adding the new.
+Some dead code is left behind especially in architecture ptrace.c files;
+this is left in to be reused under new interfaces by later patches,
+reducing the total disruption to the code.
+
+The "real_parent" member in task_struct is renamed "parent", and the old
+"parent" member is gone. That is to say, nothing interferes in the normal
+parent/child links any more. The fact that ptrace could cause these links
+to change was the source of numerous race conditions and similar bugs
+uncovered and worked around (often contortedly) in the core task management
+code under the old ptrace implementation.
+
+In each place in core code that referred to the old ptrace stuff, we now
+have the tracehook interface. These declarations are in <linux/tracehook.h>,
+and use "tracehook_*" function names. This centralizes all the hooks
+that a thread tracing facility needs into core code, and the file
+documents the calling environment (locking conditions, etc.) and meaning
+of each hook. The definitions here are all stubs doing nothing, so
+there are no user debugging features in the kernel at all. They provide
+the placeholders for where a tracing interface can tie into the kernel.
+
+The architecture support for single-step (and block-step) from the old
+ptrace support is moved into tracehook_* function interfaces. Nothing
+yet calls these, but this provides the clean interface that user
+debugging support can use for the architecture-specific single-step
+control code.
+
+Signed-off-by: Roland McGrath <roland at redhat.com>
+
+---
+
+ fs/binfmt_flat.c | 3
+ fs/binfmt_som.c | 2
+ fs/proc/base.c | 17 +
+ fs/proc/array.c | 12 +
+ fs/binfmt_elf_fdpic.c | 7 -
+ fs/exec.c | 11 -
+ fs/binfmt_elf.c | 6 -
+ fs/binfmt_aout.c | 6 -
+ security/selinux/hooks.c | 54 +++--
+ security/selinux/include/objsec.h | 1
+ arch/s390/kernel/compat_linux.c | 3
+ arch/s390/kernel/process.c | 3
+ arch/frv/kernel/ptrace.c | 15 -
+ arch/i386/math-emu/fpu_entry.c | 6 -
+ arch/i386/kernel/ptrace.c | 104 ++-------
+ arch/i386/kernel/signal.c | 37 +--
+ arch/i386/kernel/process.c | 3
+ arch/i386/kernel/vm86.c | 7 -
+ arch/i386/kernel/entry.S | 7 -
+ arch/x86_64/ia32/sys_ia32.c | 5
+ arch/x86_64/ia32/ptrace32.c | 2
+ arch/x86_64/ia32/ia32_aout.c | 6 -
+ arch/x86_64/ia32/ia32_signal.c | 7 -
+ arch/x86_64/ia32/ia32entry.S | 4
+ arch/x86_64/kernel/traps.c | 8 -
+ arch/x86_64/kernel/ptrace.c | 57 ++---
+ arch/x86_64/kernel/signal.c | 28 +-
+ arch/x86_64/kernel/process.c | 5
+ arch/x86_64/kernel/entry.S | 8 -
+ arch/x86_64/mm/fault.c | 4
+ arch/arm/kernel/ptrace.c | 17 -
+ arch/mips/kernel/sysirix.c | 2
+ arch/mips/kernel/ptrace.c | 21 --
+ arch/powerpc/kernel/sys_ppc32.c | 5
+ arch/powerpc/kernel/ptrace-common.h | 16 -
+ arch/powerpc/kernel/ptrace32.c | 13 +
+ arch/powerpc/kernel/ptrace.c | 76 +-----
+ arch/powerpc/kernel/asm-offsets.c | 2
+ arch/powerpc/kernel/signal_64.c | 3
+ arch/powerpc/kernel/process.c | 5
+ arch/powerpc/kernel/signal_32.c | 3
+ arch/arm26/kernel/ptrace.c | 32 +--
+ arch/alpha/kernel/asm-offsets.c | 2
+ arch/alpha/kernel/entry.S | 4
+ arch/sparc64/kernel/binfmt_aout32.c | 2
+ arch/sparc64/kernel/process.c | 3
+ arch/sparc64/kernel/sys_sparc32.c | 3
+ arch/ppc/kernel/asm-offsets.c | 2
+ arch/ia64/kernel/mca.c | 2
+ arch/ia64/kernel/asm-offsets.c | 2
+ arch/ia64/kernel/fsys.S | 16 +
+ kernel/exit.c | 244 +++++----------------
+ kernel/ptrace.c | 299 +------------------------
+ kernel/timer.c | 6 -
+ kernel/signal.c | 212 ++++--------------
+ kernel/tsacct.c | 2
+ kernel/sys.c | 2
+ kernel/fork.c | 66 ++----
+ include/linux/ptrace.h | 18 --
+ include/linux/sched.h | 16 -
+ include/linux/init_task.h | 3
+ include/linux/tracehook.h | 414 +++++++++++++++++++++++++++++++++++
+ include/asm-i386/tracehook.h | 52 ++++
+ include/asm-i386/thread_info.h | 7 -
+ include/asm-i386/signal.h | 4
+ include/asm-powerpc/tracehook.h | 74 ++++++
+ include/asm-x86_64/tracehook.h | 51 ++++
+ include/asm-x86_64/thread_info.h | 3
+ drivers/connector/cn_proc.c | 4
+ mm/nommu.c | 4
+ 70 files changed, 934 insertions(+), 1216 deletions(-)
+ create include/linux/tracehook.h
+ create include/asm-i386/tracehook.h
+ create include/asm-powerpc/tracehook.h
+ create include/asm-x86_64/tracehook.h
+
+--- linux-2.6/fs/binfmt_flat.c
++++ linux-2.6/fs/binfmt_flat.c
+@@ -914,9 +914,6 @@ static int load_flat_binary(struct linux
+
+ start_thread(regs, start_addr, current->mm->start_stack);
+
+- if (current->ptrace & PT_PTRACED)
+- send_sig(SIGTRAP, current, 0);
+-
+ return 0;
+ }
+
+--- linux-2.6/fs/binfmt_som.c
++++ linux-2.6/fs/binfmt_som.c
+@@ -285,8 +285,6 @@ load_som_binary(struct linux_binprm * bp
+ map_hpux_gateway_page(current,current->mm);
+
+ start_thread_som(regs, som_entry, bprm->p);
+- if (current->ptrace & PT_PTRACED)
+- send_sig(SIGTRAP, current, 0);
+ return 0;
+
+ /* error cleanup */
+--- linux-2.6/fs/proc/base.c
++++ linux-2.6/fs/proc/base.c
+@@ -67,6 +67,7 @@
+ #include <linux/mount.h>
+ #include <linux/security.h>
+ #include <linux/ptrace.h>
++#include <linux/tracehook.h>
+ #include <linux/seccomp.h>
+ #include <linux/cpuset.h>
+ #include <linux/audit.h>
+@@ -192,13 +193,6 @@ static int proc_root_link(struct inode *
+ return result;
+ }
+
+-#define MAY_PTRACE(task) \
+- (task == current || \
+- (task->parent == current && \
+- (task->ptrace & PT_PTRACED) && \
+- (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \
+- security_ptrace(current,task) == 0))
+-
+ static int proc_pid_environ(struct task_struct *task, char * buffer)
+ {
+ int res = 0;
+@@ -523,7 +517,8 @@ static ssize_t mem_read(struct file * fi
+ if (!task)
+ goto out_no_task;
+
+- if (!MAY_PTRACE(task) || !ptrace_may_attach(task))
++ if (!tracehook_allow_access_process_vm(task)
++ || !ptrace_may_attach(task))
+ goto out;
+
+ ret = -ENOMEM;
+@@ -549,7 +544,8 @@ static ssize_t mem_read(struct file * fi
+
+ this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ retval = access_process_vm(task, src, page, this_len, 0);
+- if (!retval || !MAY_PTRACE(task) || !ptrace_may_attach(task)) {
++ if (!retval || !tracehook_allow_access_process_vm(task)
++ || !ptrace_may_attach(task)) {
+ if (!ret)
+ ret = -EIO;
+ break;
+@@ -593,7 +589,8 @@ static ssize_t mem_write(struct file * f
+ if (!task)
+ goto out_no_task;
+
+- if (!MAY_PTRACE(task) || !ptrace_may_attach(task))
++ if (!tracehook_allow_access_process_vm(task)
++ || !ptrace_may_attach(task))
+ goto out;
+
+ copied = -ENOMEM;
+--- linux-2.6/fs/proc/array.c
++++ linux-2.6/fs/proc/array.c
+@@ -73,6 +73,7 @@
+ #include <linux/file.h>
+ #include <linux/times.h>
[...3607 lines suppressed...]
+ /* work to do on any return to u-space */
+ #define _TIF_ALLWORK_MASK (0x0000FFFF & ~_TIF_SECCOMP)
+
+--- linux-2.6/include/asm-i386/signal.h
++++ linux-2.6/include/asm-i386/signal.h
+@@ -221,10 +221,8 @@ struct pt_regs;
+
+ #define ptrace_signal_deliver(regs, cookie) \
+ do { \
+- if (current->ptrace & PT_DTRACE) { \
+- current->ptrace &= ~PT_DTRACE; \
++ if (test_and_clear_thread_flag(TIF_FORCED_TF)) \
+ (regs)->eflags &= ~TF_MASK; \
+- } \
+ } while (0)
+
+ #endif /* __KERNEL__ */
+--- linux-2.6/include/asm-powerpc/tracehook.h
++++ linux-2.6/include/asm-powerpc/tracehook.h
+@@ -0,0 +1,74 @@
++/*
++ * Tracing hooks, PowerPC CPU support
++ *
++ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ */
++
++#ifndef _ASM_TRACEHOOK_H
++#define _ASM_TRACEHOOK_H 1
++
++#include <linux/sched.h>
++#include <asm/ptrace.h>
++
++/*
++ * See linux/tracehook.h for the descriptions of what these need to do.
++ */
++
++#define ARCH_HAS_SINGLE_STEP (1)
++
++static inline void tracehook_enable_single_step(struct task_struct *task)
++{
++ struct pt_regs *regs = task->thread.regs;
++ if (regs != NULL) {
++#if defined(CONFIG_PPC32) && (defined(CONFIG_40x) || defined(CONFIG_BOOKE))
++ task->thread.dbcr0 = DBCR0_IDM | DBCR0_IC;
++ regs->msr |= MSR_DE;
++#else
++ regs->msr |= MSR_SE;
++#endif
++ }
++ set_tsk_thread_flag(task, TIF_SINGLESTEP);
++}
++
++static inline void tracehook_disable_single_step(struct task_struct *task)
++{
++ struct pt_regs *regs = task->thread.regs;
++ if (regs != NULL) {
++#if defined(CONFIG_PPC32) && (defined(CONFIG_40x) || defined(CONFIG_BOOKE))
++ task->thread.dbcr0 = 0;
++ regs->msr &= ~MSR_DE;
++#else
++ regs->msr &= ~MSR_SE;
++#endif
++ }
++ clear_tsk_thread_flag(task, TIF_SINGLESTEP);
++}
++
++static inline int tracehook_single_step_enabled(struct task_struct *tsk)
++{
++ return test_tsk_thread_flag(tsk, TIF_SINGLESTEP);
++}
++
++static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
++{
++ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
++{
++ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_abort_syscall(struct pt_regs *regs)
++{
++ regs->orig_gpr3 = -1L;
++}
++
++
++#endif
+--- linux-2.6/include/asm-x86_64/tracehook.h
++++ linux-2.6/include/asm-x86_64/tracehook.h
+@@ -0,0 +1,51 @@
++/*
++ * Tracing hooks, x86-64 CPU support
++ *
++ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ */
++
++#ifndef _ASM_TRACEHOOK_H
++#define _ASM_TRACEHOOK_H 1
++
++#include <linux/sched.h>
++#include <asm/ptrace.h>
++
++/*
++ * See linux/tracehook.h for the descriptions of what these need to do.
++ */
++
++#define ARCH_HAS_SINGLE_STEP (1)
++
++/* These two are defined in arch/x86_64/kernel/ptrace.c. */
++void tracehook_enable_single_step(struct task_struct *tsk);
++void tracehook_disable_single_step(struct task_struct *tsk);
++
++static inline int tracehook_single_step_enabled(struct task_struct *tsk)
++{
++ return test_tsk_thread_flag(tsk, TIF_SINGLESTEP);
++}
++
++static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
++{
++ set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
++{
++ clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
++}
++
++#define tracehook_syscall_callno(regs) (&(regs)->orig_rax)
++#define tracehook_syscall_retval(regs) (&(regs)->rax)
++static inline void tracehook_abort_syscall(struct pt_regs *regs)
++{
++ regs->orig_rax = -1L;
++}
++
++#endif
+--- linux-2.6/include/asm-x86_64/thread_info.h
++++ linux-2.6/include/asm-x86_64/thread_info.h
+@@ -115,7 +115,7 @@ static inline struct thread_info *stack_
+ #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
+ #define TIF_SECCOMP 8 /* secure computing */
+ #define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal */
+-/* 16 free */
++#define TIF_FORCED_TF 16 /* true if TF in eflags artificially */
+ #define TIF_IA32 17 /* 32bit process */
+ #define TIF_FORK 18 /* ret_from_fork */
+ #define TIF_ABI_PENDING 19
+@@ -133,6 +133,7 @@ static inline struct thread_info *stack_
+ #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
+ #define _TIF_SECCOMP (1<<TIF_SECCOMP)
+ #define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
++#define _TIF_FORCED_TF (1<<TIF_FORCED_TF)
+ #define _TIF_IA32 (1<<TIF_IA32)
+ #define _TIF_FORK (1<<TIF_FORK)
+ #define _TIF_ABI_PENDING (1<<TIF_ABI_PENDING)
+--- linux-2.6/drivers/connector/cn_proc.c
++++ linux-2.6/drivers/connector/cn_proc.c
+@@ -63,8 +63,8 @@ void proc_fork_connector(struct task_str
+ ktime_get_ts(&ts); /* get high res monotonic timestamp */
+ put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+ ev->what = PROC_EVENT_FORK;
+- ev->event_data.fork.parent_pid = task->real_parent->pid;
+- ev->event_data.fork.parent_tgid = task->real_parent->tgid;
++ ev->event_data.fork.parent_pid = task->parent->pid;
++ ev->event_data.fork.parent_tgid = task->parent->tgid;
+ ev->event_data.fork.child_pid = task->pid;
+ ev->event_data.fork.child_tgid = task->tgid;
+
+--- linux-2.6/mm/nommu.c
++++ linux-2.6/mm/nommu.c
+@@ -20,7 +20,7 @@
+ #include <linux/pagemap.h>
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+-#include <linux/ptrace.h>
++#include <linux/tracehook.h>
+ #include <linux/blkdev.h>
+ #include <linux/backing-dev.h>
+ #include <linux/mount.h>
+@@ -682,7 +682,7 @@ static unsigned long determine_vm_flags(
+ * it's being traced - otherwise breakpoints set in it may interfere
+ * with another untraced process
+ */
+- if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED))
++ if ((flags & MAP_PRIVATE) && tracehook_expect_breakpoints(current))
+ vm_flags &= ~VM_MAYSHARE;
+
+ return vm_flags;
Index: kernel-2.6.spec
===================================================================
RCS file: /cvs/dist/rpms/kernel/FC-6/kernel-2.6.spec,v
retrieving revision 1.2991
retrieving revision 1.2992
diff -u -r1.2991 -r1.2992
--- kernel-2.6.spec 17 Jul 2007 20:01:52 -0000 1.2991
+++ kernel-2.6.spec 19 Jul 2007 17:46:35 -0000 1.2992
@@ -506,7 +506,24 @@
%if !%{nopatches}
-Patch10: linux-2.6-utrace.patch
+Patch10: linux-2.6-utrace-tracehook.patch
+Patch11: linux-2.6-utrace-tracehook-ia64.patch
+Patch12: linux-2.6-utrace-tracehook-sparc64.patch
+Patch13: linux-2.6-utrace-tracehook-s390.patch
+Patch14: linux-2.6-utrace-tracehook-um.patch
+Patch15: linux-2.6-utrace-tracehook-avr32.patch
+Patch16: linux-2.6-utrace-regset.patch
+Patch17: linux-2.6-utrace-regset-ia64.patch
+Patch18: linux-2.6-utrace-regset-sparc64.patch
+Patch19: linux-2.6-utrace-regset-s390.patch
+Patch20: linux-2.6-utrace-regset-avr32.patch
+Patch21: linux-2.6-utrace-core.patch
+Patch22: linux-2.6-utrace-ptrace-compat.patch
+Patch23: linux-2.6-utrace-ptrace-compat-ia64.patch
+Patch24: linux-2.6-utrace-ptrace-compat-sparc64.patch
+Patch25: linux-2.6-utrace-ptrace-compat-s390.patch
+Patch26: linux-2.6-utrace-ptrace-compat-avr32.patch
+
Patch30: linux-2.6-sysrq-c.patch
Patch40: linux-2.6-x86-tune-generic.patch
Patch50: linux-2.6-x86-vga-vidfail.patch
@@ -570,6 +587,7 @@
Patch666: linux-2.6-libata-unbreak-smart-2.patch
Patch667: linux-2.6-libata-ata_piix_fix_pio-mwdma-programming.patch
Patch668: linux-2.6-libata_pata_atiixp_add_ati_sb700.patch
+Patch669: linux-2.6-libata-restore-combined-mode.patch
Patch680: git-wireless-dev.patch
Patch690: linux-2.6-e1000-ich9.patch
Patch710: linux-2.6-bcm43xx-pci-neuter.patch
@@ -1040,7 +1058,24 @@
ApplyPatch linux-2.6-sched-cfs.patch
# Roland's utrace ptrace replacement.
-ApplyPatch linux-2.6-utrace.patch -F2
+ApplyPatch linux-2.6-utrace-tracehook.patch
+ApplyPatch linux-2.6-utrace-tracehook-ia64.patch
+ApplyPatch linux-2.6-utrace-tracehook-sparc64.patch
+ApplyPatch linux-2.6-utrace-tracehook-s390.patch
+ApplyPatch linux-2.6-utrace-tracehook-um.patch
+ApplyPatch linux-2.6-utrace-tracehook-avr32.patch
+ApplyPatch linux-2.6-utrace-regset.patch
+ApplyPatch linux-2.6-utrace-regset-ia64.patch
+ApplyPatch linux-2.6-utrace-regset-sparc64.patch
+ApplyPatch linux-2.6-utrace-regset-s390.patch
+ApplyPatch linux-2.6-utrace-regset-avr32.patch
+ApplyPatch linux-2.6-utrace-core.patch
+ApplyPatch linux-2.6-utrace-ptrace-compat.patch
+ApplyPatch linux-2.6-utrace-ptrace-compat-ia64.patch
+ApplyPatch linux-2.6-utrace-ptrace-compat-sparc64.patch
+ApplyPatch linux-2.6-utrace-ptrace-compat-s390.patch
+ApplyPatch linux-2.6-utrace-ptrace-compat-avr32.patch
+
# setuid /proc/self/maps fix. (dependent on utrace)
ApplyPatch linux-2.6-proc-self-maps-fix.patch
@@ -1214,6 +1249,8 @@
ApplyPatch linux-2.6-libata-ata_piix_fix_pio-mwdma-programming.patch
# add ID for sb700 to the pata driver
ApplyPatch linux-2.6-libata_pata_atiixp_add_ati_sb700.patch
+# restore the ugly libata COMBINED_MODE hacks
+ApplyPatch linux-2.6-libata-restore-combined-mode.patch
# Add the new wireless stack and drivers from wireless-dev
ApplyPatch git-wireless-dev.patch
@@ -2181,6 +2218,12 @@
%endif
%changelog
+* Thu Jul 19 2007 Chuck Ebbert <cebbert at redhat.com>
+- restore the libata COMBINED_MODE hacks
+- set CONFIG_NF_CONNTRACK_PROC_COMPAT
+- restore the old firewire stack
+- new utrace backport
+
* Tue Jul 17 2007 Chuck Ebbert <cebbert at redhat.com>
- disable the nohz and highres kernel options
by default -- enable with 'nohz=yes highres=yes'
More information about the fedora-cvs-commits
mailing list