This is a large series of patches, but there are only a couple
that you need to read in detail to know how to get started on
cleaning up your arch code (1, 4, 6).
user_regset is a new kernel-internal interface into the arch
code for accessing the user-space view of machine-specific
state (registers et al--everything machine-specific that is
visible via ptrace and the like, or should be). The idea is
that arch code will have just one place it has to support
fetching and changing the user-visible machine state of a
user thread. This same interface can be used for writing
core dumps, to underlie the implementation of PTRACE_GETREGS,
PTRACE_SETREGS, and the like, and by any new set of debugging
facilities that might come along.
Why you should like this if you are an arch maintainer:
1. One place to maintain the guts of extracting thread state, get
rid of ELF_CORE_* macros, ugly ptrace implementation bits.
2. Easier way to support 32-on-64 compat for core dump formats.
3. Clean up your arch ptrace code, have less magic to maintain.
Also see http://lwn.net/Articles/259841/ to adapt your arch
support for single-step into the generic entry points so you
can use the arch-independent ptrace code to do more dirty work.
4. When a new CPU variant comes along with a new set of registers
(new FPUs and vector units, etc), just one place to add support
and one format for the data, automatically covers core dumps and
later debugging interfaces.
5. When new core kernel changes come along for better debugging
facilities, you won't have to do anything to have them supported
on your arch. When you provide user_regset data structures and
entry points, you've done your part.
Why you should like this if you don't touch arch code:
1. One place to consolidate all you needed to know about whatever
arch, just check its user_regset formats to see what people
versed in this arch think userland needs to have access to.
2. Without something like it, new and better facilities for user
process debugging are intractable to implement and you won't get any.
I've settled on the machine-specific data formats used in ELF core
dump files as the standard for the data layouts intended to be seen by
userland. My logic is as follows. There should be a single canonical
user format for each kind of machine state (general registers, FPU
registers, etc.)--that's just common sense. There are two current
precedents for formats already known to userland for these things,
being the ELF core dump formats and the ptrace formats, and one of
those is a pile of crap. So, user_regset data formats are, by
definition, "note" data formats for ELF core dumps, and they're
distinguished by the n_type codes (NT_* constants); regset 0 has to be
elf_gregset_t, which is embedded in the NT_PRSTATUS note. All other
regset notes are nothing but the contents of the regset, the machine
state of the thread (e.g. NT_PRFPREG). You can define user_regset
flavors that don't set an n_type code if you want to. But it's
worthwhile to let the debugger examine some chunk of thread state,
then it's also worthwhile to have it in a dump for examination after
the fact, so you might as well just add a new NT_<CPU>_* constant.
(Picking the constant and flipping a switch is all it takes to cause
a new user_regset flavor to appear in core dumps.)
Patches 1 through 9 affect only machine-independent code.
These add the infrastructure that revamped arch code uses.
The new code has no effect on any arch that does not change
its code to define some new macros or use some new functions.
Patches 10 through 25 affect only arch/powerpc code. I have
also CC'd these to linux-arch to give an example of what the
arch code looks like when you clean things up to work via the
user_regset interfaces. The powerpc code was already pretty
clean, so the changes are fairly easy to understand even if
you aren't familiar with powerpc.
Patches 26 through 43 affect only arch/x86 code. I have not
CC'd these ones to linux-arch. They include a bunch of
cleanup that is specific to the idiosyncracies of the x86
code and isn't interesting as an example for what another
arch would do.
I'd be glad to help anyone interested in adapting more arch code
to the user_regset style with the details.
Thanks,
Roland
This pulls out the code for writing the notes segment of an ELF core dump
into separate functions. This cleanly isolates into one cluster of
functions everything that deals with the note formats and the hooks into
arch code to fill them. The top-level elf_core_dump function itself now
deals purely with the generic ELF format and the memory segments.
This only moves code around into functions that can be inlined away.
It should not change any behavior at all.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
fs/binfmt_elf.c | 324 +++++++++++++++++++++++++++++++++----------------------
1 files changed, 194 insertions(+), 130 deletions(-)
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index efabb37..3500358 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1395,7 +1395,8 @@ static int writenote(struct memelfnote *men, struct file *file,
if (!dump_seek(file, (off))) \
goto end_coredump;
-static void fill_elf_header(struct elfhdr *elf, int segs)
+static void fill_elf_header(struct elfhdr *elf, int segs,
+ u16 machine, u32 flags, u8 osabi)
{
memcpy(elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELF_CLASS;
@@ -1405,12 +1406,12 @@ static void fill_elf_header(struct elfhdr *elf, int segs)
memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
- elf->e_machine = ELF_ARCH;
+ elf->e_machine = machine;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof(struct elfhdr);
elf->e_shoff = 0;
- elf->e_flags = ELF_CORE_EFLAGS;
+ elf->e_flags = flags;
elf->e_ehsize = sizeof(struct elfhdr);
elf->e_phentsize = sizeof(struct elf_phdr);
elf->e_phnum = segs;
@@ -1517,6 +1518,16 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
return 0;
}
+static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm)
+{
+ elf_addr_t *auxv = (elf_addr_t *) mm->saved_auxv;
+ int i = 0;
+ do
+ i += 2;
+ while (auxv[i - 2] != AT_NULL);
+ fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv);
+}
+
/* Here is the structure in which status of each thread is captured. */
struct elf_thread_status
{
@@ -1569,6 +1580,174 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
return sz;
}
+struct elf_note_info {
+ struct memelfnote *notes;
+ struct elf_prstatus *prstatus; /* NT_PRSTATUS */
+ struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */
+ struct list_head thread_list;
+ elf_fpregset_t *fpu;
+#ifdef ELF_CORE_COPY_XFPREGS
+ elf_fpxregset_t *xfpu;
+#endif
+ int thread_status_size;
+ int numnote;
+};
+
+static int fill_note_info(struct elfhdr *elf, int phdrs,
+ struct elf_note_info *info,
+ long signr, struct pt_regs *regs)
+{
+#define NUM_NOTES 6
+ struct list_head *t;
+ struct task_struct *g, *p;
+
+ info->notes = NULL;
+ info->prstatus = NULL;
+ info->psinfo = NULL;
+ info->fpu = NULL;
+#ifdef ELF_CORE_COPY_XFPREGS
+ info->xfpu = NULL;
+#endif
+ INIT_LIST_HEAD(&info->thread_list);
+
+ info->notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote),
+ GFP_KERNEL);
+ if (!info->notes)
+ return 0;
+ info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL);
+ if (!info->psinfo)
+ return 0;
+ info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL);
+ if (!info->prstatus)
+ return 0;
+ info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL);
+ if (!info->fpu)
+ return 0;
+#ifdef ELF_CORE_COPY_XFPREGS
+ info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL);
+ if (!info->xfpu)
+ return 0;
+#endif
+
+ info->thread_status_size = 0;
+ if (signr) {
+ struct elf_thread_status *tmp;
+ rcu_read_lock();
+ do_each_thread(g, p)
+ if (current->mm == p->mm && current != p) {
+ tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC);
+ if (!tmp) {
+ rcu_read_unlock();
+ return 0;
+ }
+ tmp->thread = p;
+ list_add(&tmp->list, &info->thread_list);
+ }
+ while_each_thread(g, p);
+ rcu_read_unlock();
+ list_for_each(t, &info->thread_list) {
+ struct elf_thread_status *tmp;
+ int sz;
+
+ tmp = list_entry(t, struct elf_thread_status, list);
+ sz = elf_dump_thread_status(signr, tmp);
+ info->thread_status_size += sz;
+ }
+ }
+ /* now collect the dump for the current */
+ memset(info->prstatus, 0, sizeof(*info->prstatus));
+ fill_prstatus(info->prstatus, current, signr);
+ elf_core_copy_regs(&info->prstatus->pr_reg, regs);
+
+ /* Set up header */
+ fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS, ELF_OSABI);
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+
+ fill_note(info->notes + 0, "CORE", NT_PRSTATUS,
+ sizeof(*info->prstatus), info->prstatus);
+ fill_psinfo(info->psinfo, current->group_leader, current->mm);
+ fill_note(info->notes + 1, "CORE", NT_PRPSINFO,
+ sizeof(*info->psinfo), info->psinfo);
+
+ info->numnote = 2;
+
+ fill_auxv_note(&info->notes[info->numnote++], current->mm);
+
+ /* Try to dump the FPU. */
+ info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs,
+ info->fpu);
+ if (info->prstatus->pr_fpvalid)
+ fill_note(info->notes + info->numnote++,
+ "CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu);
+#ifdef ELF_CORE_COPY_XFPREGS
+ if (elf_core_copy_task_xfpregs(current, info->xfpu))
+ fill_note(info->notes + info->numnote++,
+ "LINUX", ELF_CORE_XFPREG_TYPE,
+ sizeof(*info->xfpu), info->xfpu);
+#endif
+
+ return 1;
+
+#undef NUM_NOTES
+}
+
+static size_t get_note_info_size(struct elf_note_info *info)
+{
+ int sz = 0;
+ int i;
+
+ for (i = 0; i < info->numnote; i++)
+ sz += notesize(info->notes + i);
+
+ sz += info->thread_status_size;
+
+ return sz;
+}
+
+static int write_note_info(struct elf_note_info *info,
+ struct file *file, loff_t *foffset)
+{
+ int i;
+ struct list_head *t;
+
+ for (i = 0; i < info->numnote; i++)
+ if (!writenote(info->notes + i, file, foffset))
+ return 0;
+
+ /* write out the thread status notes section */
+ list_for_each(t, &info->thread_list) {
+ struct elf_thread_status *tmp =
+ list_entry(t, struct elf_thread_status, list);
+
+ for (i = 0; i < tmp->num_notes; i++)
+ if (!writenote(&tmp->notes[i], file, foffset))
+ return 0;
+ }
+
+ return 1;
+}
+
+static void free_note_info(struct elf_note_info *info)
+{
+ while (!list_empty(&info->thread_list)) {
+ struct list_head *tmp = info->thread_list.next;
+ list_del(tmp);
+ kfree(list_entry(tmp, struct elf_thread_status, list));
+ }
+
+ kfree(info->prstatus);
+ kfree(info->psinfo);
+ kfree(info->notes);
+ kfree(info->fpu);
+#ifdef ELF_CORE_COPY_XFPREGS
+ kfree(info->xfpu);
+#endif
+}
+
static struct vm_area_struct *first_vma(struct task_struct *tsk,
struct vm_area_struct *gate_vma)
{
@@ -1604,29 +1783,15 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
*/
static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
{
-#define NUM_NOTES 6
int has_dumped = 0;
mm_segment_t fs;
int segs;
size_t size = 0;
- int i;
struct vm_area_struct *vma, *gate_vma;
struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff, foffset;
- int numnote;
- struct memelfnote *notes = NULL;
- struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */
- struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */
- struct task_struct *g, *p;
- LIST_HEAD(thread_list);
- struct list_head *t;
- elf_fpregset_t *fpu = NULL;
-#ifdef ELF_CORE_COPY_XFPREGS
- elf_fpxregset_t *xfpu = NULL;
-#endif
- int thread_status_size = 0;
- elf_addr_t *auxv;
unsigned long mm_flags;
+ struct elf_note_info info;
/*
* We no longer stop all VM operations.
@@ -1644,52 +1809,6 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
elf = kmalloc(sizeof(*elf), GFP_KERNEL);
if (!elf)
goto cleanup;
- prstatus = kmalloc(sizeof(*prstatus), GFP_KERNEL);
- if (!prstatus)
- goto cleanup;
- psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
- if (!psinfo)
- goto cleanup;
- notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), GFP_KERNEL);
- if (!notes)
- goto cleanup;
- fpu = kmalloc(sizeof(*fpu), GFP_KERNEL);
- if (!fpu)
- goto cleanup;
-#ifdef ELF_CORE_COPY_XFPREGS
- xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL);
- if (!xfpu)
- goto cleanup;
-#endif
-
- if (signr) {
- struct elf_thread_status *tmp;
- rcu_read_lock();
- do_each_thread(g,p)
- if (current->mm == p->mm && current != p) {
- tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC);
- if (!tmp) {
- rcu_read_unlock();
- goto cleanup;
- }
- tmp->thread = p;
- list_add(&tmp->list, &thread_list);
- }
- while_each_thread(g,p);
- rcu_read_unlock();
- list_for_each(t, &thread_list) {
- struct elf_thread_status *tmp;
- int sz;
-
- tmp = list_entry(t, struct elf_thread_status, list);
- sz = elf_dump_thread_status(signr, tmp);
- thread_status_size += sz;
- }
- }
- /* now collect the dump for the current */
- memset(prstatus, 0, sizeof(*prstatus));
- fill_prstatus(prstatus, current, signr);
- elf_core_copy_regs(&prstatus->pr_reg, regs);
segs = current->mm->map_count;
#ifdef ELF_CORE_EXTRA_PHDRS
@@ -1700,42 +1819,16 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
if (gate_vma != NULL)
segs++;
- /* Set up header */
- fill_elf_header(elf, segs + 1); /* including notes section */
-
- has_dumped = 1;
- current->flags |= PF_DUMPCORE;
-
/*
- * Set up the notes in similar form to SVR4 core dumps made
- * with info from their /proc.
+ * Collect all the non-memory information about the process for the
+ * notes. This also sets up the file header.
*/
+ if (!fill_note_info(elf, segs + 1, /* including notes section */
+ &info, signr, regs))
+ goto cleanup;
- fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus);
- fill_psinfo(psinfo, current->group_leader, current->mm);
- fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
-
- numnote = 2;
-
- auxv = (elf_addr_t *)current->mm->saved_auxv;
-
- i = 0;
- do
- i += 2;
- while (auxv[i - 2] != AT_NULL);
- fill_note(¬es[numnote++], "CORE", NT_AUXV,
- i * sizeof(elf_addr_t), auxv);
-
- /* Try to dump the FPU. */
- if ((prstatus->pr_fpvalid =
- elf_core_copy_task_fpregs(current, regs, fpu)))
- fill_note(notes + numnote++,
- "CORE", NT_PRFPREG, sizeof(*fpu), fpu);
-#ifdef ELF_CORE_COPY_XFPREGS
- if (elf_core_copy_task_xfpregs(current, xfpu))
- fill_note(notes + numnote++,
- "LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu);
-#endif
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
fs = get_fs();
set_fs(KERNEL_DS);
@@ -1748,12 +1841,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
/* Write notes phdr entry */
{
struct elf_phdr phdr;
- int sz = 0;
-
- for (i = 0; i < numnote; i++)
- sz += notesize(notes + i);
-
- sz += thread_status_size;
+ size_t sz = get_note_info_size(&info);
sz += elf_coredump_extra_notes_size();
@@ -1798,23 +1886,12 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
#endif
/* write out the notes section */
- for (i = 0; i < numnote; i++)
- if (!writenote(notes + i, file, &foffset))
- goto end_coredump;
+ if (!write_note_info(&info, file, &foffset))
+ goto end_coredump;
if (elf_coredump_extra_notes_write(file, &foffset))
goto end_coredump;
- /* write out the thread status notes section */
- list_for_each(t, &thread_list) {
- struct elf_thread_status *tmp =
- list_entry(t, struct elf_thread_status, list);
-
- for (i = 0; i < tmp->num_notes; i++)
- if (!writenote(&tmp->notes[i], file, &foffset))
- goto end_coredump;
- }
-
/* Align to page */
DUMP_SEEK(dataoff - foffset);
@@ -1865,22 +1942,9 @@ end_coredump:
set_fs(fs);
cleanup:
- while (!list_empty(&thread_list)) {
- struct list_head *tmp = thread_list.next;
- list_del(tmp);
- kfree(list_entry(tmp, struct elf_thread_status, list));
- }
-
kfree(elf);
- kfree(prstatus);
- kfree(psinfo);
- kfree(notes);
- kfree(fpu);
-#ifdef ELF_CORE_COPY_XFPREGS
- kfree(xfpu);
-#endif
+ free_note_info(&info);
return has_dumped;
-#undef NUM_NOTES
}
#endif /* USE_ELF_CORE_DUMP */
--
1.5.3.6
This modifies the ELF core dump code under #ifdef CORE_DUMP_USE_REGSET.
It changes nothing when this macro is not defined. When it's #define'd
by some arch header (e.g. asm/elf.h), the arch must support the
user_regset (linux/regset.h) interface for reading thread state.
This provides an alternate version of note segment writing that is based
purely on the user_regset interfaces. When CORE_DUMP_USE_REGSET is set,
the arch need not define macros such as ELF_CORE_COPY_REGS and ELF_ARCH.
All that information is taken from the user_regset data structures.
The core dumps come out exactly the same if arch's definitions for its
user_regset details are correct.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
fs/binfmt_elf.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 224 insertions(+), 0 deletions(-)
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 3500358..47fda1e 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1528,6 +1528,228 @@ static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm)
fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv);
}
+#ifdef CORE_DUMP_USE_REGSET
+#include <linux/regset.h>
+
+struct elf_thread_core_info {
+ struct elf_thread_core_info *next;
+ struct task_struct *task;
+ struct elf_prstatus prstatus;
+ struct memelfnote notes[0];
+};
+
+struct elf_note_info {
+ struct elf_thread_core_info *thread;
+ struct memelfnote psinfo;
+ struct memelfnote auxv;
+ size_t size;
+ int thread_notes;
+};
+
+static int fill_thread_core_info(struct elf_thread_core_info *t,
+ const struct user_regset_view *view,
+ long signr, size_t *total)
+{
+ unsigned int i;
+
+ /*
+ * NT_PRSTATUS is the one special case, because the regset data
+ * goes into the pr_reg field inside the note contents, rather
+ * than being the whole note contents. We fill the reset in here.
+ * We assume that regset 0 is NT_PRSTATUS.
+ */
+ fill_prstatus(&t->prstatus, t->task, signr);
+ (void) view->regsets[0].get(t->task, &view->regsets[0],
+ 0, sizeof(t->prstatus.pr_reg),
+ &t->prstatus.pr_reg, NULL);
+
+ fill_note(&t->notes[0], "CORE", NT_PRSTATUS,
+ sizeof(t->prstatus), &t->prstatus);
+ *total += notesize(&t->notes[0]);
+
+ /*
+ * Each other regset might generate a note too. For each regset
+ * that has no core_note_type or is inactive, we leave t->notes[i]
+ * all zero and we'll know to skip writing it later.
+ */
+ for (i = 1; i < view->n; ++i) {
+ const struct user_regset *regset = &view->regsets[i];
+ if (regset->core_note_type &&
+ (!regset->active || regset->active(t->task, regset))) {
+ int ret;
+ size_t size = regset->n * regset->size;
+ void *data = kmalloc(size, GFP_KERNEL);
+ if (unlikely(!data))
+ return 0;
+ ret = regset->get(t->task, regset,
+ 0, size, data, NULL);
+ if (unlikely(ret))
+ kfree(data);
+ else {
+ if (regset->core_note_type != NT_PRFPREG)
+ fill_note(&t->notes[i], "LINUX",
+ regset->core_note_type,
+ size, data);
+ else {
+ t->prstatus.pr_fpvalid = 1;
+ fill_note(&t->notes[i], "CORE",
+ NT_PRFPREG, size, data);
+ }
+ *total += notesize(&t->notes[i]);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int fill_note_info(struct elfhdr *elf, int phdrs,
+ struct elf_note_info *info,
+ long signr, struct pt_regs *regs)
+{
+ struct task_struct *dump_task = current;
+ const struct user_regset_view *view = task_user_regset_view(dump_task);
+ struct elf_thread_core_info *t;
+ struct elf_prpsinfo *psinfo;
+ struct task_struct *g, *p;
+ unsigned int i;
+
+ info->size = 0;
+ info->thread = NULL;
+
+ psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
+ fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
+
+ if (psinfo == NULL)
+ return 0;
+
+ /*
+ * Figure out how many notes we're going to need for each thread.
+ */
+ info->thread_notes = 0;
+ for (i = 0; i < view->n; ++i)
+ if (view->regsets[i].core_note_type != 0)
+ ++info->thread_notes;
+
+ /*
+ * Sanity check. We rely on regset 0 being in NT_PRSTATUS,
+ * since it is our one special case.
+ */
+ if (unlikely(info->thread_notes == 0) ||
+ unlikely(view->regsets[0].core_note_type != NT_PRSTATUS)) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ /*
+ * Initialize the ELF file header.
+ */
+ fill_elf_header(elf, phdrs,
+ view->e_machine, view->e_flags, view->ei_osabi);
+
+ /*
+ * Allocate a structure for each thread.
+ */
+ rcu_read_lock();
+ do_each_thread(g, p)
+ if (p->mm == dump_task->mm) {
+ t = kzalloc(offsetof(struct elf_thread_core_info,
+ notes[info->thread_notes]),
+ GFP_ATOMIC);
+ if (unlikely(!t)) {
+ rcu_read_unlock();
+ return 0;
+ }
+ t->task = p;
+ if (p == dump_task || !info->thread) {
+ t->next = info->thread;
+ info->thread = t;
+ } else {
+ /*
+ * Make sure to keep the original task at
+ * the head of the list.
+ */
+ t->next = info->thread->next;
+ info->thread->next = t;
+ }
+ }
+ while_each_thread(g, p);
+ rcu_read_unlock();
+
+ /*
+ * Now fill in each thread's information.
+ */
+ for (t = info->thread; t != NULL; t = t->next)
+ if (!fill_thread_core_info(t, view, signr, &info->size))
+ return 0;
+
+ /*
+ * Fill in the two process-wide notes.
+ */
+ fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm);
+ info->size += notesize(&info->psinfo);
+
+ fill_auxv_note(&info->auxv, current->mm);
+ info->size += notesize(&info->auxv);
+
+ return 1;
+}
+
+static size_t get_note_info_size(struct elf_note_info *info)
+{
+ return info->size;
+}
+
+/*
+ * Write all the notes for each thread. When writing the first thread, the
+ * process-wide notes are interleaved after the first thread-specific note.
+ */
+static int write_note_info(struct elf_note_info *info,
+ struct file *file, loff_t *foffset)
+{
+ bool first = 1;
+ struct elf_thread_core_info *t = info->thread;
+
+ do {
+ int i;
+
+ if (!writenote(&t->notes[0], file, foffset))
+ return 0;
+
+ if (first && !writenote(&info->psinfo, file, foffset))
+ return 0;
+ if (first && !writenote(&info->auxv, file, foffset))
+ return 0;
+
+ for (i = 1; i < info->thread_notes; ++i)
+ if (t->notes[i].data &&
+ !writenote(&t->notes[i], file, foffset))
+ return 0;
+
+ first = 0;
+ t = t->next;
+ } while (t);
+
+ return 1;
+}
+
+static void free_note_info(struct elf_note_info *info)
+{
+ struct elf_thread_core_info *threads = info->thread;
+ while (threads) {
+ unsigned int i;
+ struct elf_thread_core_info *t = threads;
+ threads = t->next;
+ WARN_ON(t->notes[0].data && t->notes[0].data != &t->prstatus);
+ for (i = 1; i < info->thread_notes; ++i)
+ kfree(t->notes[i].data);
+ kfree(t);
+ }
+ kfree(info->psinfo.data);
+}
+
+#else
+
/* Here is the structure in which status of each thread is captured. */
struct elf_thread_status
{
@@ -1748,6 +1970,8 @@ static void free_note_info(struct elf_note_info *info)
#endif
}
+#endif
+
static struct vm_area_struct *first_vma(struct task_struct *tsk,
struct vm_area_struct *gate_vma)
{
--
1.5.3.6
The new header <linux/regset.h> defines the types struct user_regset and
struct user_regset_view, with some associated declarations. This new set
of interfaces will become the standard way for arch code to expose
user-mode machine-specific state. A single set of entry points into arch
code can do all the low-level work in one place to fill the needs of core
dumps, ptrace, and any other user-mode debugging facilities that might come
along in the future.
For existing arch code to adapt to the user_regset interfaces, each arch
can work from the code it already has to support core files and ptrace.
The formats you want for user_regset are the core file formats. The only
wrinkle in adapting old ptrace implementation code as user_regset get and
set functions is that these functions can be called on current as well as
on another task_struct that is stopped and switched out as for ptrace.
For some kinds of machine state, you may have to load it directly from CPU
registers or otherwise differently for current than for another thread.
(Your core dump support already handles this in elf_core_copy_regs for
current and elf_core_copy_task_regs for other tasks, so just check there.)
The set function should also be made to work on current in case that
entails some special cases, though this was never required before for
ptrace. Adding this flexibility covers the arch needs to open the door to
more sophisticated new debugging facilities that don't always need to
context-switch to do every little thing.
The copyin/copyout helper functions (in a later patch) relieve the arch
code of most of the cumbersome details of the flexible get/set interfaces.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/linux/regset.h | 206 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 206 insertions(+), 0 deletions(-)
diff --git a/include/linux/regset.h b/include/linux/regset.h
new file mode 100644
index 0000000..85d0fb0
--- /dev/null
+++ b/include/linux/regset.h
@@ -0,0 +1,206 @@
+/*
+ * User-mode machine state access
+ *
+ * Copyright (C) 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 _LINUX_REGSET_H
+#define _LINUX_REGSET_H 1
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+struct task_struct;
+struct user_regset;
+
+
+/**
+ * user_regset_active_fn - type of @active function in &struct user_regset
+ * @target: thread being examined
+ * @regset: regset being examined
+ *
+ * Return -%ENODEV if not available on the hardware found.
+ * Return %0 if no interesting state in this thread.
+ * Return >%0 number of @size units of interesting state.
+ * Any get call fetching state beyond that number will
+ * see the default initialization state for this data,
+ * so a caller that knows what the default state is need
+ * not copy it all out.
+ * This call is optional; the pointer is %NULL if there
+ * is no inexpensive check to yield a value < @n.
+ */
+typedef int user_regset_active_fn(struct task_struct *target,
+ const struct user_regset *regset);
+
+/**
+ * user_regset_get_fn - type of @get function in &struct user_regset
+ * @target: thread being examined
+ * @regset: regset being examined
+ * @pos: offset into the regset data to access, in bytes
+ * @count: amount of data to copy, in bytes
+ * @kbuf: if not %NULL, a kernel-space pointer to copy into
+ * @ubuf: if @kbuf is %NULL, a user-space pointer to copy into
+ *
+ * Fetch register values. Return %0 on success; -%EIO or -%ENODEV
+ * are usual failure returns. The @pos and @count values are in
+ * bytes, but must be properly aligned. If @kbuf is non-null, that
+ * buffer is used and @ubuf is ignored. If @kbuf is %NULL, then
+ * ubuf gives a userland pointer to access directly, and an -%EFAULT
+ * return value is possible.
+ */
+typedef int user_regset_get_fn(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf);
+
+/**
+ * user_regset_set_fn - type of @set function in &struct user_regset
+ * @target: thread being examined
+ * @regset: regset being examined
+ * @pos: offset into the regset data to access, in bytes
+ * @count: amount of data to copy, in bytes
+ * @kbuf: if not %NULL, a kernel-space pointer to copy from
+ * @ubuf: if @kbuf is %NULL, a user-space pointer to copy from
+ *
+ * Store register values. Return %0 on success; -%EIO or -%ENODEV
+ * are usual failure returns. The @pos and @count values are in
+ * bytes, but must be properly aligned. If @kbuf is non-null, that
+ * buffer is used and @ubuf is ignored. If @kbuf is %NULL, then
+ * ubuf gives a userland pointer to access directly, and an -%EFAULT
+ * return value is possible.
+ */
+typedef int user_regset_set_fn(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf);
+
+/**
+ * user_regset_writeback_fn - type of @writeback function in &struct user_regset
+ * @target: thread being examined
+ * @regset: regset being examined
+ * @immediate: zero if writeback at completion of next context switch is OK
+ *
+ * This call is optional; usually the pointer is %NULL. When
+ * provided, there is some user memory associated with this regset's
+ * hardware, such as memory backing cached register data on register
+ * window machines; the regset's data controls what user memory is
+ * used (e.g. via the stack pointer value).
+ *
+ * Write register data back to user memory. If the @immediate flag
+ * is nonzero, it must be written to the user memory so uaccess or
+ * access_process_vm() can see it when this call returns; if zero,
+ * then it must be written back by the time the task completes a
+ * context switch (as synchronized with wait_task_inactive()).
+ * Return %0 on success or if there was nothing to do, -%EFAULT for
+ * a memory problem (bad stack pointer or whatever), or -%EIO for a
+ * hardware problem.
+ */
+typedef int user_regset_writeback_fn(struct task_struct *target,
+ const struct user_regset *regset,
+ int immediate);
+
+/**
+ * struct user_regset - accessible thread CPU state
+ * @n: Number of slots (registers).
+ * @size: Size in bytes of a slot (register).
+ * @align: Required alignment, in bytes.
+ * @bias: Bias from natural indexing.
+ * @core_note_type: ELF note @n_type value used in core dumps.
+ * @get: Function to fetch values.
+ * @set: Function to store values.
+ * @active: Function to report if regset is active, or %NULL.
+ * @writeback: Function to write data back to user memory, or %NULL.
+ *
+ * This data structure describes a machine resource we call a register set.
+ * This is part of the state of an individual thread, not necessarily
+ * actual CPU registers per se. A register set consists of a number of
+ * similar slots, given by @n. Each slot is @size bytes, and aligned to
+ * @align bytes (which is at least @size).
+ *
+ * These functions must be called only on the current thread or on a
+ * thread that is in %TASK_STOPPED or %TASK_TRACED state, that we are
+ * guaranteed will not be woken up and return to user mode, and that we
+ * have called wait_task_inactive() on. (The target thread always might
+ * wake up for SIGKILL while these functions are working, in which case
+ * that thread's user_regset state might be scrambled.)
+ *
+ * The @pos argument must be aligned according to @align; the @count
+ * argument must be a multiple of @size. These functions are not
+ * responsible for checking for invalid arguments.
+ *
+ * When there is a natural value to use as an index, @bias gives the
+ * difference between the natural index and the slot index for the
+ * register set. For example, x86 GDT segment descriptors form a regset;
+ * the segment selector produces a natural index, but only a subset of
+ * that index space is available as a regset (the TLS slots); subtracting
+ * @bias from a segment selector index value computes the regset slot.
+ *
+ * If nonzero, @core_note_type gives the n_type field (NT_* value)
+ * of the core file note in which this regset's data appears.
+ * NT_PRSTATUS is a special case in that the regset data starts at
+ * offsetof(struct elf_prstatus, pr_reg) into the note data; that is
+ * part of the per-machine ELF formats userland knows about. In
+ * other cases, the core file note contains exactly the whole regset
+ * (@n * @size) and nothing else. The core file note is normally
+ * omitted when there is an @active function and it returns zero.
+ */
+struct user_regset {
+ user_regset_get_fn *get;
+ user_regset_set_fn *set;
+ user_regset_active_fn *active;
+ user_regset_writeback_fn *writeback;
+ unsigned int n;
+ unsigned int size;
+ unsigned int align;
+ unsigned int bias;
+ unsigned int core_note_type;
+};
+
+/**
+ * struct user_regset_view - available regsets
+ * @name: Identifier, e.g. UTS_MACHINE string.
+ * @regsets: Array of @n regsets available in this view.
+ * @n: Number of elements in @regsets.
+ * @e_machine: ELF header @e_machine %EM_* value written in core dumps.
+ * @e_flags: ELF header @e_flags value written in core dumps.
+ * @ei_osabi: ELF header @e_ident[%EI_OSABI] value written in core dumps.
+ *
+ * A regset view is a collection of regsets (&struct user_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 user_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 user_regset_view {
+ const char *name;
+ const struct user_regset *regsets;
+ unsigned int n;
+ u32 e_flags;
+ u16 e_machine;
+ u8 ei_osabi;
+};
+
+/*
+ * This is documented here rather than at the definition sites because its
+ * implementation is machine-dependent but its interface is universal.
+ */
+/**
+ * task_user_regset_view - Return the process's native regset view.
+ * @tsk: a thread of the process in question
+ *
+ * Return the &struct user_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 user_regset_view *task_user_regset_view(struct task_struct *tsk);
+
+
+#endif /* <linux/regset.h> */
--
1.5.3.6
This adds some inlines to linux/regset.h intended for arch code to use in
its user_regset get and set functions. These make it pretty easy to deal
with the interface's optional kernel-space or user-space pointers and its
generalized access to a part of the register data at a time.
In simple cases where the internal data structure matches the exported
layout (core dump format), a get function can be nothing but a call to
user_regset_copyout, and a set function a call to user_regset_copyin.
In other cases the exported layout is usually made up of a few pieces each
stored contiguously in a different internal data structure. These helpers
make it straightforward to write a get or set function by processing each
contiguous chunk of the data in order. The start_pos and end_pos arguments
are always constants, so these inlines collapse to a small amount of code.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/linux/regset.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 116 insertions(+), 0 deletions(-)
diff --git a/include/linux/regset.h b/include/linux/regset.h
index 85d0fb0..761c931 100644
--- a/include/linux/regset.h
+++ b/include/linux/regset.h
@@ -15,6 +15,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/uaccess.h>
struct task_struct;
struct user_regset;
@@ -203,4 +204,119 @@ struct user_regset_view {
const struct user_regset_view *task_user_regset_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 user_regset_copyout(unsigned int *pos, unsigned int *count,
+ void **kbuf,
+ void __user **ubuf, const void *data,
+ const int start_pos, const 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 user_regset_copyin(unsigned int *pos, unsigned int *count,
+ const void **kbuf,
+ const void __user **ubuf, void *data,
+ const int start_pos, const 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 user_regset_copyout_zero(unsigned int *pos,
+ unsigned int *count,
+ void **kbuf, void __user **ubuf,
+ const int start_pos,
+ const 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 user_regset_copyin_ignore(unsigned int *pos,
+ unsigned int *count,
+ const void **kbuf,
+ const void __user **ubuf,
+ const int start_pos,
+ const 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;
+}
+
+
#endif /* <linux/regset.h> */
--
1.5.3.6
This defines two new inlines in linux/regset.h, for use in arch_ptrace
implementations and the like. These provide simplified wrappers for using
the user_regset interfaces to copy thread regset data into the caller's
user-space memory. The inlines are trivial, but make the common uses in
places such as ptrace implementation much more concise, easier to read, and
less prone to code-copying errors.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/linux/regset.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 46 insertions(+), 0 deletions(-)
diff --git a/include/linux/regset.h b/include/linux/regset.h
index 761c931..8abee65 100644
--- a/include/linux/regset.h
+++ b/include/linux/regset.h
@@ -318,5 +318,51 @@ static inline int user_regset_copyin_ignore(unsigned int *pos,
return 0;
}
+/**
+ * copy_regset_to_user - fetch a thread's user_regset data into user memory
+ * @target: thread to be examined
+ * @view: &struct user_regset_view describing user thread machine state
+ * @setno: index in @view->regsets
+ * @offset: offset into the regset data, in bytes
+ * @size: amount of data to copy, in bytes
+ * @data: user-mode pointer to copy into
+ */
+static inline int copy_regset_to_user(struct task_struct *target,
+ const struct user_regset_view *view,
+ unsigned int setno,
+ unsigned int offset, unsigned int size,
+ void __user *data)
+{
+ const struct user_regset *regset = &view->regsets[setno];
+
+ if (!access_ok(VERIFY_WRITE, data, size))
+ return -EIO;
+
+ return regset->get(target, regset, offset, size, NULL, data);
+}
+
+/**
+ * copy_regset_from_user - store into thread's user_regset data from user memory
+ * @target: thread to be examined
+ * @view: &struct user_regset_view describing user thread machine state
+ * @setno: index in @view->regsets
+ * @offset: offset into the regset data, in bytes
+ * @size: amount of data to copy, in bytes
+ * @data: user-mode pointer to copy from
+ */
+static inline int copy_regset_from_user(struct task_struct *target,
+ const struct user_regset_view *view,
+ unsigned int setno,
+ unsigned int offset, unsigned int size,
+ const void __user *data)
+{
+ const struct user_regset *regset = &view->regsets[setno];
+
+ if (!access_ok(VERIFY_READ, data, size))
+ return -EIO;
+
+ return regset->set(target, regset, offset, size, NULL, data);
+}
+
#endif /* <linux/regset.h> */
--
1.5.3.6
This adds fs/compat_binfmt_elf.c, a wrapper around fs/binfmt_elf.c for
32-bit ELF support on 64-bit kernels. It can replace all the hand-rolled
versions of this that each 32/64 arch has, which are all about the same.
To use this, an arch's asm/elf.h has to define at least a few compat_*
macros that parallel the various macros that fs/binfmt_elf.c uses for
native support.
There is no attempt to deal with compat macros for the core dump format
support. To use this file, the arch has to define compat_gregset_t for
linux/elfcore-compat.h and #define CORE_DUMP_USE_REGSET. The 32-bit
compatible formats should come automatically from task_user_regset_view
called on a 32-bit task.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
fs/compat_binfmt_elf.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 131 insertions(+), 0 deletions(-)
diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c
new file mode 100644
index 0000000..0adced2
--- /dev/null
+++ b/fs/compat_binfmt_elf.c
@@ -0,0 +1,131 @@
+/*
+ * 32-bit compatibility support for ELF format executables and core dumps.
+ *
+ * Copyright (C) 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.
+ *
+ * This file is used in a 64-bit kernel that wants to support 32-bit ELF.
+ * asm/elf.h is responsible for defining the compat_* and COMPAT_* macros
+ * used below, with definitions appropriate for 32-bit ABI compatibility.
+ *
+ * We use macros to rename the ABI types and machine-dependent
+ * functions used in binfmt_elf.c to compat versions.
+ */
+
+#include <linux/elfcore-compat.h>
+#include <linux/time.h>
+
+/*
+ * Rename the basic ELF layout types to refer to the 32-bit class of files.
+ */
+#undef ELF_CLASS
+#define ELF_CLASS ELFCLASS32
+
+#undef elfhdr
+#undef elf_phdr
+#undef elf_note
+#undef elf_addr_t
+#define elfhdr elf32_hdr
+#define elf_phdr elf32_phdr
+#define elf_note elf32_note
+#define elf_addr_t Elf32_Addr
+
+/*
+ * The machine-dependent core note format types are defined in elfcore-compat.h,
+ * which requires asm/elf.h to define compat_elf_gregset_t et al.
+ */
+#define elf_prstatus compat_elf_prstatus
+#define elf_prpsinfo compat_elf_prpsinfo
+
+/*
+ * Compat version of cputime_to_compat_timeval, perhaps this
+ * should be an inline in <linux/compat.h>.
+ */
+static void cputime_to_compat_timeval(const cputime_t cputime,
+ struct compat_timeval *value)
+{
+ struct timeval tv;
+ cputime_to_timeval(cputime, &tv);
+ value->tv_sec = tv.tv_sec;
+ value->tv_usec = tv.tv_usec;
+}
+
+#undef cputime_to_timeval
+#define cputime_to_timeval cputime_to_compat_timeval
+
+
+/*
+ * To use this file, asm/elf.h must define compat_elf_check_arch.
+ * The other following macros can be defined if the compat versions
+ * differ from the native ones, or omitted when they match.
+ */
+
+#undef ELF_ARCH
+#undef elf_check_arch
+#define elf_check_arch compat_elf_check_arch
+
+#ifdef COMPAT_ELF_PLATFORM
+#undef ELF_PLATFORM
+#define ELF_PLATFORM COMPAT_ELF_PLATFORM
+#endif
+
+#ifdef COMPAT_ELF_HWCAP
+#undef ELF_HWCAP
+#define ELF_HWCAP COMPAT_ELF_HWCAP
+#endif
+
+#ifdef COMPAT_ARCH_DLINFO
+#undef ARCH_DLINFO
+#define ARCH_DLINFO COMPAT_ARCH_DLINFO
+#endif
+
+#ifdef COMPAT_ELF_ET_DYN_BASE
+#undef ELF_ET_DYN_BASE
+#define ELF_ET_DYN_BASE COMPAT_ELF_ET_DYN_BASE
+#endif
+
+#ifdef COMPAT_ELF_EXEC_PAGESIZE
+#undef ELF_EXEC_PAGESIZE
+#define ELF_EXEC_PAGESIZE COMPAT_ELF_EXEC_PAGESIZE
+#endif
+
+#ifdef COMPAT_ELF_PLAT_INIT
+#undef ELF_PLAT_INIT
+#define ELF_PLAT_INIT COMPAT_ELF_PLAT_INIT
+#endif
+
+#ifdef COMPAT_SET_PERSONALITY
+#undef SET_PERSONALITY
+#define SET_PERSONALITY COMPAT_SET_PERSONALITY
+#endif
+
+#ifdef compat_start_thread
+#undef start_thread
+#define start_thread compat_start_thread
+#endif
+
+#ifdef compat_arch_setup_additional_pages
+#undef ARCH_HAS_SETUP_ADDITIONAL_PAGES
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+#undef arch_setup_additional_pages
+#define arch_setup_additional_pages compat_arch_setup_additional_pages
+#endif
+
+/*
+ * Rename a few of the symbols that binfmt_elf.c will define.
+ * These are all local so the names don't really matter, but it
+ * might make some debugging less confusing not to duplicate them.
+ */
+#define elf_format compat_elf_format
+#define init_elf_binfmt init_compat_elf_binfmt
+#define exit_elf_binfmt exit_compat_elf_binfmt
+
+/*
+ * We share all the actual code with the native (64-bit) version.
+ */
+#include "binfmt_elf.c"
--
1.5.3.6
This makes ptrace_request handle {PEEK,POKE}{TEXT,DATA} directly.
Every arch_ptrace that could call generic_ptrace_peekdata already
has a default case calling ptrace_request, so this keeps things
simpler for the arch code.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
kernel/ptrace.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 2824726..e521042 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -426,6 +426,13 @@ int ptrace_request(struct task_struct *child, long request,
int ret = -EIO;
switch (request) {
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ return generic_ptrace_peekdata(child, addr, data);
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ return generic_ptrace_pokedata(child, addr, data);
+
#ifdef PTRACE_OLDSETOPTIONS
case PTRACE_OLDSETOPTIONS:
#endif
--
1.5.3.6
This adds a compat_ptrace_request that is the analogue of ptrace_request
for the things that 32-on-64 ptrace implementations can share in common.
So far there are just a couple of requests handled generically.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/linux/compat.h | 4 ++++
kernel/ptrace.c | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h
index ba29d4c..e70a2a1 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -271,5 +271,9 @@ asmlinkage long compat_sys_signalfd(int ufd,
asmlinkage long compat_sys_timerfd(int ufd, int clockid, int flags,
const struct compat_itimerspec __user *utmr);
+extern int compat_ptrace_request(struct task_struct *child,
+ compat_long_t request,
+ compat_ulong_t addr, compat_ulong_t data);
+
#endif /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index e521042..bc1a764 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -605,3 +605,41 @@ int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
return (copied == sizeof(data)) ? 0 : -EIO;
}
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+int compat_ptrace_request(struct task_struct *child, compat_long_t request,
+ compat_ulong_t addr, compat_ulong_t data)
+{
+ compat_ulong_t __user *datap = compat_ptr(data);
+ compat_ulong_t word;
+ int ret;
+
+ switch (request) {
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ ret = access_process_vm(child, addr, &word, sizeof(word), 0);
+ if (ret != sizeof(word))
+ ret = -EIO;
+ else
+ ret = put_user(word, datap);
+ break;
+
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ ret = access_process_vm(child, addr, &data, sizeof(data), 1);
+ ret = (ret != sizeof(data) ? -EIO : 0);
+ break;
+
+ case PTRACE_GETEVENTMSG:
+ ret = put_user((compat_ulong_t) child->ptrace_message, datap);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ }
+
+ return ret;
+}
+#endif /* CONFIG_COMPAT */
--
1.5.3.6
This adds a generic definition of compat_sys_ptrace that calls
compat_arch_ptrace, parallel to sys_ptrace/arch_ptrace. Some
machines needing this already define a function by that name.
The new generic function is defined only on machines that
put #define __ARCH_WANT_COMPAT_SYS_PTRACE into asm/ptrace.h.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/linux/compat.h | 7 +++++++
kernel/ptrace.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+), 0 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h
index e70a2a1..aa3a55e 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -275,5 +275,12 @@ extern int compat_ptrace_request(struct task_struct *child,
compat_long_t request,
compat_ulong_t addr, compat_ulong_t data);
+#ifdef __ARCH_WANT_COMPAT_SYS_PTRACE
+extern long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
+ compat_ulong_t addr, compat_ulong_t data);
+asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
+ compat_long_t addr, compat_long_t data);
+#endif /* __ARCH_WANT_COMPAT_SYS_PTRACE */
+
#endif /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index bc1a764..319ab4a 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -642,4 +642,50 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
return ret;
}
+
+#ifdef __ARCH_WANT_COMPAT_SYS_PTRACE
+asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
+ compat_long_t addr, compat_long_t data)
+{
+ struct task_struct *child;
+ long ret;
+
+ /*
+ * This lock_kernel fixes a subtle race with suid exec
+ */
+ 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;
+ }
+
+ if (request == PTRACE_ATTACH) {
+ ret = ptrace_attach(child);
+ /*
+ * Some architectures need to do book-keeping after
+ * a ptrace attach.
+ */
+ if (!ret)
+ arch_ptrace_attach(child);
+ goto out_put_task_struct;
+ }
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (!ret)
+ ret = compat_arch_ptrace(child, request, addr, data);
+
+ out_put_task_struct:
+ put_task_struct(child);
+ out:
+ unlock_kernel();
+ return ret;
+}
+#endif /* __ARCH_WANT_COMPAT_SYS_PTRACE */
+
#endif /* CONFIG_COMPAT */
--
1.5.3.6
This isolates the ptrace code for the special-case registers msr and trap
from the ptrace-layout dispatch code. This should inline away completely.
It cleanly separates the low-level machine magic that has to be done for
deep reasons, from the superficial details of the ptrace interface.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 45 ++++++++++++++++++++++++++++--------------
1 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 8c25b00..4edc118 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -60,20 +60,38 @@
#define PT_MAX_PUT_REG PT_CCR
#endif
+static unsigned long get_user_msr(struct task_struct *task)
+{
+ return task->thread.regs->msr | task->thread.fpexc_mode;
+}
+
+static int set_user_msr(struct task_struct *task, unsigned long msr)
+{
+ task->thread.regs->msr &= ~MSR_DEBUGCHANGE;
+ task->thread.regs->msr |= msr & MSR_DEBUGCHANGE;
+ return 0;
+}
+
+/*
+ * We prevent mucking around with the reserved area of trap
+ * which are used internally by the kernel.
+ */
+static int set_user_trap(struct task_struct *task, unsigned long trap)
+{
+ task->thread.regs->trap = trap & 0xfff0;
+ return 0;
+}
+
/*
* Get contents of register REGNO in task TASK.
*/
unsigned long ptrace_get_reg(struct task_struct *task, int regno)
{
- unsigned long tmp = 0;
-
if (task->thread.regs == NULL)
return -EIO;
- if (regno == PT_MSR) {
- tmp = ((unsigned long *)task->thread.regs)[PT_MSR];
- return tmp | task->thread.fpexc_mode;
- }
+ if (regno == PT_MSR)
+ return get_user_msr(task);
if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long)))
return ((unsigned long *)task->thread.regs)[regno];
@@ -89,15 +107,12 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data)
if (task->thread.regs == NULL)
return -EIO;
- if (regno <= PT_MAX_PUT_REG || regno == PT_TRAP) {
- if (regno == PT_MSR)
- data = (data & MSR_DEBUGCHANGE)
- | (task->thread.regs->msr & ~MSR_DEBUGCHANGE);
- /* We prevent mucking around with the reserved area of trap
- * which are used internally by the kernel
- */
- if (regno == PT_TRAP)
- data &= 0xfff0;
+ if (regno == PT_MSR)
+ return set_user_msr(task, data);
+ if (regno == PT_TRAP)
+ return set_user_trap(task, data);
+
+ if (regno <= PT_MAX_PUT_REG) {
((unsigned long *)task->thread.regs)[regno] = data;
return 0;
}
--
1.5.3.6
Remove some dead code we no longer need now that the
user_regset interfaces are doing all these jobs.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/process.c | 48 -----------------------------------------
include/asm-powerpc/elf.h | 46 ---------------------------------------
2 files changed, 0 insertions(+), 94 deletions(-)
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index b9d8837..9c2983c 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -104,17 +104,6 @@ void enable_kernel_fp(void)
}
EXPORT_SYMBOL(enable_kernel_fp);
-int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
-{
- if (!tsk->thread.regs)
- return 0;
- flush_fp_to_thread(current);
-
- memcpy(fpregs, &tsk->thread.fpr[0], sizeof(*fpregs));
-
- return 1;
-}
-
#ifdef CONFIG_ALTIVEC
void enable_kernel_altivec(void)
{
@@ -148,35 +137,6 @@ void flush_altivec_to_thread(struct task_struct *tsk)
preempt_enable();
}
}
-
-int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs)
-{
- /* ELF_NVRREG includes the VSCR and VRSAVE which we need to save
- * separately, see below */
- const int nregs = ELF_NVRREG - 2;
- elf_vrreg_t *reg;
- u32 *dest;
-
- if (tsk == current)
- flush_altivec_to_thread(tsk);
-
- reg = (elf_vrreg_t *)vrregs;
-
- /* copy the 32 vr registers */
- memcpy(reg, &tsk->thread.vr[0], nregs * sizeof(*reg));
- reg += nregs;
-
- /* copy the vscr */
- memcpy(reg, &tsk->thread.vscr, sizeof(*reg));
- reg++;
-
- /* vrsave is stored in the high 32bit slot of the final 128bits */
- memset(reg, 0, sizeof(*reg));
- dest = (u32 *)reg;
- *dest = tsk->thread.vrsave;
-
- return 1;
-}
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_SPE
@@ -209,14 +169,6 @@ void flush_spe_to_thread(struct task_struct *tsk)
preempt_enable();
}
}
-
-int dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs)
-{
- flush_spe_to_thread(current);
- /* We copy u32 evr[32] + u64 acc + u32 spefscr -> 35 */
- memcpy(evrregs, ¤t->thread.evr[0], sizeof(u32) * 35);
- return 1;
-}
#endif /* CONFIG_SPE */
#ifndef CONFIG_SMP
diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h
index 9080d85..fe309b4 100644
--- a/include/asm-powerpc/elf.h
+++ b/include/asm-powerpc/elf.h
@@ -178,52 +178,6 @@ typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32];
#define ELF_ET_DYN_BASE (0x20000000)
-/*
- * Our registers are always unsigned longs, whether we're a 32 bit
- * process or 64 bit, on either a 64 bit or 32 bit kernel.
- *
- * This macro relies on elf_regs[i] having the right type to truncate to,
- * either u32 or u64. It defines the body of the elf_core_copy_regs
- * function, either the native one with elf_gregset_t elf_regs or
- * the 32-bit one with elf_gregset_t32 elf_regs.
- */
-#define PPC_ELF_CORE_COPY_REGS(elf_regs, regs) \
- int i, nregs = min(sizeof(*regs) / sizeof(unsigned long), \
- (size_t)ELF_NGREG); \
- for (i = 0; i < nregs; i++) \
- elf_regs[i] = ((unsigned long *) regs)[i]; \
- memset(&elf_regs[i], 0, (ELF_NGREG - i) * sizeof(elf_regs[0]))
-
-/* Common routine for both 32-bit and 64-bit native processes */
-static inline void ppc_elf_core_copy_regs(elf_gregset_t elf_regs,
- struct pt_regs *regs)
-{
- PPC_ELF_CORE_COPY_REGS(elf_regs, regs);
-}
-#define ELF_CORE_COPY_REGS(gregs, regs) ppc_elf_core_copy_regs(gregs, regs);
-
-static inline int dump_task_regs(struct task_struct *tsk,
- elf_gregset_t *elf_regs)
-{
- struct pt_regs *regs = tsk->thread.regs;
- if (regs)
- ppc_elf_core_copy_regs(*elf_regs, regs);
-
- return 1;
-}
-#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs)
-
-extern int dump_task_fpu(struct task_struct *, elf_fpregset_t *);
-#define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs)
-
-typedef elf_vrregset_t elf_fpxregset_t;
-
-#ifdef CONFIG_ALTIVEC
-extern int dump_task_altivec(struct task_struct *, elf_vrregset_t *vrregs);
-#define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_altivec(tsk, regs)
-#define ELF_CORE_XFPREG_TYPE NT_PPC_VMX
-#endif
-
#endif /* __KERNEL__ */
/* ELF_HWCAP yields a mask that user programs can use to figure out what
--
1.5.3.6
This switches powerpc to using the user_regset-based code for ELF core
dumps. The core dumps come out exactly the same either way, except that
the NT_PPC_VMX note is now omitted for any thread that never touched its
Altivec registers (thread_struct.vr_used).
Signed-off-by: Roland McGrath <[email protected]>
---
include/asm-powerpc/elf.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h
index 6bd07ef..fd9bf8b 100644
--- a/include/asm-powerpc/elf.h
+++ b/include/asm-powerpc/elf.h
@@ -167,6 +167,7 @@ typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32];
#define elf_check_arch(x) ((x)->e_machine == ELF_ARCH)
#define USE_ELF_CORE_DUMP
+#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE PAGE_SIZE
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
--
1.5.3.6
This makes the SPE register data appear in ELF core dumps,
using the new n_type value NT_PPC_SPE (0x101). This new
note type is not used by any consumers of core files yet,
but support can be added. I don't even have any hardware
with SPE capabilities, so I've never seen such a note.
But this demonstrates how simple it is to export register
information in core dumps when the user_regset style is
used for the low-level code.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 2 +-
include/linux/elf.h | 1 +
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index e961e10..0231e7d 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -642,7 +642,7 @@ static const struct user_regset compat_regsets[] = {
#endif
#ifdef CONFIG_SPE
[REGSET_SPE] = {
- .n = 35,
+ .core_note_type = NT_PPC_SPE, .n = 35,
.size = sizeof(u32), .align = sizeof(u32),
.active = evr_active, .get = evr_get, .set = evr_set
},
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 576e83b..ba268b2 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -355,6 +355,7 @@ typedef struct elf64_shdr {
#define NT_AUXV 6
#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */
#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
+#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
/* Note header in a PT_NOTE section */
--
1.5.3.6
This implements user_regset-style accessors for the powerpc general registers.
In the future these functions will be the only place that needs to understand
the user_regset layout (core dump format) and how it maps to the internal
representation of user thread state.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 91 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 4edc118..e493fc0 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -119,6 +119,97 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data)
return -EIO;
}
+static int gpr_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.regs,
+ 0, offsetof(struct pt_regs, msr));
+ if (!ret) {
+ unsigned long msr = get_user_msr(target);
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr,
+ offsetof(struct pt_regs, msr),
+ offsetof(struct pt_regs, msr) +
+ sizeof(msr));
+ }
+
+ BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) !=
+ offsetof(struct pt_regs, msr) + sizeof(long));
+
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.regs->orig_gpr3,
+ offsetof(struct pt_regs, orig_gpr3),
+ sizeof(struct pt_regs));
+ if (!ret)
+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ sizeof(struct pt_regs), -1);
+
+ return ret;
+}
+
+static int gpr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned long reg;
+ int ret;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.regs,
+ 0, PT_MSR * sizeof(reg));
+
+ if (!ret && count > 0) {
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®,
+ PT_MSR * sizeof(reg),
+ (PT_MSR + 1) * sizeof(reg));
+ if (!ret)
+ ret = set_user_msr(target, reg);
+ }
+
+ BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) !=
+ offsetof(struct pt_regs, msr) + sizeof(long));
+
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.regs->orig_gpr3,
+ PT_ORIG_R3 * sizeof(reg),
+ (PT_MAX_PUT_REG + 1) * sizeof(reg));
+
+ if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret)
+ ret = user_regset_copyin_ignore(
+ &pos, &count, &kbuf, &ubuf,
+ (PT_MAX_PUT_REG + 1) * sizeof(reg),
+ PT_TRAP * sizeof(reg));
+
+ if (!ret && count > 0) {
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®,
+ PT_TRAP * sizeof(reg),
+ (PT_TRAP + 1) * sizeof(reg));
+ if (!ret)
+ ret = set_user_trap(target, reg);
+ }
+
+ if (!ret)
+ ret = user_regset_copyin_ignore(
+ &pos, &count, &kbuf, &ubuf,
+ (PT_TRAP + 1) * sizeof(reg), -1);
+
+ return ret;
+}
static int fpr_get(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
--
1.5.3.6
This replaces all the code for powerpc PTRACE_*REGS* requests with
simple calls to copy_regset_from_user and copy_regset_to_user. All
the ptrace formats are either the whole corresponding user_regset
format (core dump format) or a leading subset of it, so we can get
rid of all the remaining embedded knowledge of both those layouts
and of the internal data structures they correspond to. Only the
user_reget accessors need to implement that.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 234 +++++++++++-------------------------------
1 files changed, 59 insertions(+), 175 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 0231e7d..eb27bd9 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -237,24 +237,6 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
&target->thread.fpr, 0, -1);
}
-static int get_fpregs(void __user *data, struct task_struct *task,
- int has_fpscr)
-{
- unsigned int count = has_fpscr ? 33 : 32;
- if (!access_ok(VERIFY_WRITE, data, count * sizeof(double)))
- return -EFAULT;
- return fpr_get(task, NULL, 0, count * sizeof(double), NULL, data);
-}
-
-static int set_fpregs(void __user *data, struct task_struct *task,
- int has_fpscr)
-{
- unsigned int count = has_fpscr ? 33 : 32;
- if (!access_ok(VERIFY_READ, data, count * sizeof(double)))
- return -EFAULT;
- return fpr_set(task, NULL, 0, count * sizeof(double), NULL, data);
-}
-
#ifdef CONFIG_ALTIVEC
/*
@@ -339,31 +321,6 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}
-
-/*
- * Get contents of AltiVec register state in task TASK
- */
-static int get_vrregs(unsigned long __user *data, struct task_struct *task)
-{
- if (!access_ok(VERIFY_WRITE, data,
- 33 * sizeof(vector128) + sizeof(u32)))
- return -EFAULT;
-
- return vr_get(task, NULL, 0, 33 * sizeof(vector128) + sizeof(u32),
- NULL, data);
-}
-
-/*
- * Write contents of AltiVec register state into task TASK.
- */
-static int set_vrregs(struct task_struct *task, unsigned long __user *data)
-{
- if (!access_ok(VERIFY_READ, data, 33 * sizeof(vector128) + sizeof(u32)))
- return -EFAULT;
-
- return vr_set(task, NULL, 0, 33 * sizeof(vector128) + sizeof(u32),
- NULL, data);
-}
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_SPE
@@ -430,28 +387,6 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}
-
-/*
- * Get contents of SPE register state in task TASK.
- */
-static int get_evrregs(unsigned long __user *data, struct task_struct *task)
-{
- if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(u32)))
- return -EFAULT;
-
- return evr_get(task, NULL, 0, 35 * sizeof(u32), NULL, data);
-}
-
-/*
- * Write contents of SPE register state into task TASK.
- */
-static int set_evrregs(struct task_struct *task, unsigned long *data)
-{
- if (!access_ok(VERIFY_READ, data, 35 * sizeof(u32)))
- return -EFAULT;
-
- return evr_set(task, NULL, 0, 35 * sizeof(u32), NULL, data);
-}
#endif /* CONFIG_SPE */
@@ -732,55 +667,29 @@ void ptrace_disable(struct task_struct *child)
static long arch_ptrace_old(struct task_struct *child, long request, long addr,
long data)
{
- int ret = -EPERM;
-
- switch(request) {
- case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned long __user *tmp = (unsigned long __user *)addr;
-
- CHECK_FULL_REGS(child->thread.regs);
- for (i = 0; i < 32; i++) {
- ret = put_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned long __user *tmp = (unsigned long __user *)addr;
-
- CHECK_FULL_REGS(child->thread.regs);
- for (i = 0; i < 32; i++) {
- ret = get_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
- flush_fp_to_thread(child);
- ret = get_fpregs((void __user *)addr, child, 0);
- break;
- }
-
- case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
- flush_fp_to_thread(child);
- ret = set_fpregs((void __user *)addr, child, 0);
- break;
+ switch (request) {
+ case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_GPR, 0, 32 * sizeof(long),
+ (void __user *) data);
+
+ case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_GPR, 0, 32 * sizeof(long),
+ (const void __user *) data);
+
+ case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_FPR, 0, 32 * sizeof(double),
+ (void __user *) data);
+
+ case PPC_PTRACE_SETFPREGS: /* Set FPRs 0 - 31. */
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_FPR, 0, 32 * sizeof(double),
+ (const void __user *) data);
}
- }
- return ret;
+ return -EPERM;
}
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
@@ -871,85 +780,60 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
#ifdef CONFIG_PPC64
case PTRACE_GETREGS64:
#endif
- case PTRACE_GETREGS: { /* Get all pt_regs from the child. */
- int ui;
- if (!access_ok(VERIFY_WRITE, (void __user *)data,
- sizeof(struct pt_regs))) {
- ret = -EIO;
- break;
- }
- CHECK_FULL_REGS(child->thread.regs);
- ret = 0;
- for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
- ret |= __put_user(ptrace_get_reg(child, ui),
- (unsigned long __user *) data);
- data += sizeof(long);
- }
- break;
- }
+ case PTRACE_GETREGS: /* Get all pt_regs from the child. */
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_GPR,
+ 0, sizeof(struct pt_regs),
+ (void __user *) data);
#ifdef CONFIG_PPC64
case PTRACE_SETREGS64:
#endif
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- int ui;
- if (!access_ok(VERIFY_READ, (void __user *)data,
- sizeof(struct pt_regs))) {
- ret = -EIO;
- break;
- }
- CHECK_FULL_REGS(child->thread.regs);
- ret = 0;
- for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
- ret = __get_user(tmp, (unsigned long __user *) data);
- if (ret)
- break;
- ptrace_put_reg(child, ui, tmp);
- data += sizeof(long);
- }
- break;
- }
-
- case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */
- flush_fp_to_thread(child);
- ret = get_fpregs((void __user *)data, child, 1);
- break;
- }
-
- case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */
- flush_fp_to_thread(child);
- ret = set_fpregs((void __user *)data, child, 1);
- break;
- }
+ case PTRACE_SETREGS: /* Set all gp regs in the child. */
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_GPR,
+ 0, sizeof(struct pt_regs),
+ (const void __user *) data);
+
+ case PTRACE_GETFPREGS: /* Get the child FPU state (FPR0...31 + FPSCR) */
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_FPR,
+ 0, sizeof(elf_fpregset_t),
+ (void __user *) data);
+
+ case PTRACE_SETFPREGS: /* Set the child FPU state (FPR0...31 + FPSCR) */
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_FPR,
+ 0, sizeof(elf_fpregset_t),
+ (const void __user *) data);
#ifdef CONFIG_ALTIVEC
case PTRACE_GETVRREGS:
- /* Get the child altivec register state. */
- flush_altivec_to_thread(child);
- ret = get_vrregs((unsigned long __user *)data, child);
- break;
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_VMX,
+ 0, (33 * sizeof(vector128) +
+ sizeof(u32)),
+ (void __user *) data);
case PTRACE_SETVRREGS:
- /* Set the child altivec register state. */
- flush_altivec_to_thread(child);
- ret = set_vrregs(child, (unsigned long __user *)data);
- break;
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_VMX,
+ 0, (33 * sizeof(vector128) +
+ sizeof(u32)),
+ (const void __user *) data);
#endif
#ifdef CONFIG_SPE
case PTRACE_GETEVRREGS:
/* Get the child spe register state. */
- flush_spe_to_thread(child);
- ret = get_evrregs((unsigned long __user *)data, child);
- break;
+ return copy_regset_to_user(child, &user_ppc_native_view,
+ REGSET_SPE, 0, 35 * sizeof(u32),
+ (void __user *) data);
case PTRACE_SETEVRREGS:
/* Set the child spe register state. */
- /* this is to clear the MSR_SPE bit to force a reload
- * of register state from memory */
- flush_spe_to_thread(child);
- ret = set_evrregs(child, (unsigned long __user *)data);
- break;
+ return copy_regset_from_user(child, &user_ppc_native_view,
+ REGSET_SPE, 0, 35 * sizeof(u32),
+ (const void __user *) data);
#endif
/* Old reverse args ptrace callss */
--
1.5.3.6
This removes some duplicated code by calling the new generic
compat_ptrace_request from powerpc's compat_sys_ptrace.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace32.c | 34 ++--------------------------------
1 files changed, 2 insertions(+), 32 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index fea6206..6612304 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -27,6 +27,7 @@
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>
+#include <linux/compat.h>
#include <asm/uaccess.h>
#include <asm/page.h>
@@ -112,20 +113,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
goto out_tsk;
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 int tmp;
- int copied;
-
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- break;
- ret = put_user(tmp, (u32 __user *)data);
- break;
- }
-
/*
* Read 4 bytes of the other process' storage
* data is a pointer specifying where the user wants the
@@ -225,19 +212,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
break;
}
- /* If I and D space are separate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA: {
- unsigned int tmp;
- tmp = data;
- ret = 0;
- if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1)
- == sizeof(tmp))
- break;
- ret = -EIO;
- break;
- }
-
/*
* Write 4 bytes into the other process' storage
* data is the 4 bytes that the user wants written
@@ -337,10 +311,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
break;
}
- case PTRACE_GETEVENTMSG:
- ret = put_user(child->ptrace_message, (unsigned int __user *) data);
- break;
-
case PTRACE_GETREGS: { /* Get all pt_regs from the child. */
int ui;
if (!access_ok(VERIFY_WRITE, (void __user *)data,
@@ -402,7 +372,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
break;
default:
- ret = ptrace_request(child, request, addr, data);
+ ret = compat_ptrace_request(child, request, addr, data);
break;
}
out_tsk:
--
1.5.3.6
This replaces powerpc's compat_sys_ptrace with a compat_arch_ptrace and
enables the new generic definition of compat_sys_ptrace instead.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace32.c | 33 +++++----------------------------
include/asm-powerpc/ptrace.h | 2 ++
2 files changed, 7 insertions(+), 28 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index 6612304..0f6eea0 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -85,33 +85,13 @@ static long compat_ptrace_old(struct task_struct *child, long request,
return ret;
}
-long compat_sys_ptrace(int request, int pid, unsigned long addr,
- unsigned long data)
+long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
+ compat_ulong_t caddr, compat_ulong_t cdata)
{
- struct task_struct *child;
+ unsigned long addr = caddr;
+ unsigned long data = cdata;
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;
- }
-
- 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;
-
switch (request) {
/*
* Read 4 bytes of the other process' storage
@@ -375,9 +355,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
ret = compat_ptrace_request(child, request, addr, data);
break;
}
-out_tsk:
- put_task_struct(child);
-out:
- unlock_kernel();
+
return ret;
}
diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h
index 3063363..a8cb00c 100644
--- a/include/asm-powerpc/ptrace.h
+++ b/include/asm-powerpc/ptrace.h
@@ -55,6 +55,8 @@ struct pt_regs {
#ifdef __powerpc64__
+#define __ARCH_WANT_COMPAT_SYS_PTRACE
+
#define STACK_FRAME_OVERHEAD 112 /* size of minimum stack frame */
/* Size of dummy stack frame allocated when calling signal handler. */
--
1.5.3.6
This cleans up the 32-bit ptrace syscall support to use user_regset calls
to get at the register data for PTRACE_*REGS* calls.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace32.c | 96 ++++++++++-----------------------------
1 files changed, 25 insertions(+), 71 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index 0f6eea0..4c1de6a 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -24,6 +24,7 @@
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/regset.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>
@@ -46,43 +47,21 @@
static long compat_ptrace_old(struct task_struct *child, long request,
long addr, long data)
{
- int ret = -EPERM;
-
- switch(request) {
- case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned int __user *tmp = (unsigned int __user *)addr;
-
- CHECK_FULL_REGS(child->thread.regs);
- for (i = 0; i < 32; i++) {
- ret = put_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
- }
-
- case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
- int i;
- unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
- unsigned int __user *tmp = (unsigned int __user *)addr;
-
- CHECK_FULL_REGS(child->thread.regs);
- for (i = 0; i < 32; i++) {
- ret = get_user(*reg, tmp);
- if (ret)
- break;
- reg++;
- tmp++;
- }
- break;
+ switch (request) {
+ case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
+ return copy_regset_to_user(child,
+ task_user_regset_view(current), 0,
+ 0, 32 * sizeof(compat_long_t),
+ compat_ptr(data));
+
+ case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
+ return copy_regset_from_user(child,
+ task_user_regset_view(current), 0,
+ 0, 32 * sizeof(compat_long_t),
+ compat_ptr(data));
}
- }
- return ret;
+ return -EPERM;
}
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
@@ -291,42 +270,17 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
break;
}
- case PTRACE_GETREGS: { /* Get all pt_regs from the child. */
- int ui;
- if (!access_ok(VERIFY_WRITE, (void __user *)data,
- PT_REGS_COUNT * sizeof(int))) {
- ret = -EIO;
- break;
- }
- CHECK_FULL_REGS(child->thread.regs);
- ret = 0;
- for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
- ret |= __put_user(ptrace_get_reg(child, ui),
- (unsigned int __user *) data);
- data += sizeof(int);
- }
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- int ui;
- if (!access_ok(VERIFY_READ, (void __user *)data,
- PT_REGS_COUNT * sizeof(int))) {
- ret = -EIO;
- break;
- }
- CHECK_FULL_REGS(child->thread.regs);
- ret = 0;
- for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
- ret = __get_user(tmp, (unsigned int __user *) data);
- if (ret)
- break;
- ptrace_put_reg(child, ui, tmp);
- data += sizeof(int);
- }
- break;
- }
+ case PTRACE_GETREGS: /* Get all pt_regs from the child. */
+ return copy_regset_to_user(
+ child, task_user_regset_view(current), 0,
+ 0, PT_REGS_COUNT * sizeof(compat_long_t),
+ compat_ptr(data));
+
+ case PTRACE_SETREGS: /* Set all gp regs in the child. */
+ return copy_regset_from_user(
+ child, task_user_regset_view(current), 0,
+ 0, PT_REGS_COUNT * sizeof(compat_long_t),
+ compat_ptr(data));
case PTRACE_GETFPREGS:
case PTRACE_SETFPREGS:
--
1.5.3.6
This switches x86 to the user_regset-based code for ELF core dumps.
The core dumps come out exactly the same as before.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/asm-x86/elf.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/include/asm-x86/elf.h b/include/asm-x86/elf.h
index 5e5705b..123e2d5 100644
--- a/include/asm-x86/elf.h
+++ b/include/asm-x86/elf.h
@@ -201,6 +201,7 @@ extern int vdso_enabled;
#endif /* !CONFIG_X86_32 */
+#define CORE_DUMP_USE_REGSET
#define USE_ELF_CORE_DUMP
#define ELF_EXEC_PAGESIZE 4096
--
1.5.3.6
This implements user_regset-style accessors for the powerpc Altivec data,
and rewrites the existing ptrace code in terms of those calls.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 112 +++++++++++++++++++++++++++++-------------
1 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index f1ce646..7cdf35a 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/regset.h>
+#include <linux/elf.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>
@@ -163,30 +164,87 @@ static int set_fpregs(void __user *data, struct task_struct *task,
* (combined (32- and 64-bit) gdb.
*/
+static int vr_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ flush_altivec_to_thread(target);
+ return target->thread.used_vr ? regset->n : 0;
+}
+
+static int vr_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ flush_altivec_to_thread(target);
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, vscr) !=
+ offsetof(struct thread_struct, vr[32]));
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr, 0,
+ 33 * sizeof(vector128));
+ if (!ret) {
+ /*
+ * Copy out only the low-order word of vrsave.
+ */
+ union {
+ elf_vrreg_t reg;
+ u32 word;
+ } vrsave;
+ memset(&vrsave, 0, sizeof(vrsave));
+ vrsave.word = target->thread.vrsave;
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
+ 33 * sizeof(vector128), -1);
+ }
+
+ return ret;
+}
+
+static int vr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ flush_altivec_to_thread(target);
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, vscr) !=
+ offsetof(struct thread_struct, vr[32]));
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.vr, 0, 33 * sizeof(vector128));
+ if (!ret && count > 0) {
+ /*
+ * We use only the first word of vrsave.
+ */
+ union {
+ elf_vrreg_t reg;
+ u32 word;
+ } vrsave;
+ memset(&vrsave, 0, sizeof(vrsave));
+ vrsave.word = target->thread.vrsave;
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave,
+ 33 * sizeof(vector128), -1);
+ if (!ret)
+ target->thread.vrsave = vrsave.word;
+ }
+
+ return ret;
+}
+
/*
* Get contents of AltiVec register state in task TASK
*/
static int get_vrregs(unsigned long __user *data, struct task_struct *task)
{
- unsigned long regsize;
-
- /* copy AltiVec registers VR[0] .. VR[31] */
- regsize = 32 * sizeof(vector128);
- if (copy_to_user(data, task->thread.vr, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VSCR */
- regsize = 1 * sizeof(vector128);
- if (copy_to_user(data, &task->thread.vscr, regsize))
+ if (!access_ok(VERIFY_WRITE, data,
+ 33 * sizeof(vector128) + sizeof(u32)))
return -EFAULT;
- data += (regsize / sizeof(unsigned long));
- /* copy VRSAVE */
- if (put_user(task->thread.vrsave, (u32 __user *)data))
- return -EFAULT;
-
- return 0;
+ return vr_get(task, NULL, 0, 33 * sizeof(vector128) + sizeof(u32),
+ NULL, data);
}
/*
@@ -194,25 +252,11 @@ static int get_vrregs(unsigned long __user *data, struct task_struct *task)
*/
static int set_vrregs(struct task_struct *task, unsigned long __user *data)
{
- unsigned long regsize;
-
- /* copy AltiVec registers VR[0] .. VR[31] */
- regsize = 32 * sizeof(vector128);
- if (copy_from_user(task->thread.vr, data, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VSCR */
- regsize = 1 * sizeof(vector128);
- if (copy_from_user(&task->thread.vscr, data, regsize))
- return -EFAULT;
- data += (regsize / sizeof(unsigned long));
-
- /* copy VRSAVE */
- if (get_user(task->thread.vrsave, (u32 __user *)data))
+ if (!access_ok(VERIFY_READ, data, 33 * sizeof(vector128) + sizeof(u32)))
return -EFAULT;
- return 0;
+ return vr_set(task, NULL, 0, 33 * sizeof(vector128) + sizeof(u32),
+ NULL, data);
}
#endif /* CONFIG_ALTIVEC */
--
1.5.3.6
This renames arch/x86/kernel/{i387_32.c => i387.c}.
This is a pure renaming, but paves the way for merging
the 32-bit and 64-bit versions of this code.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/Makefile_32 | 3 +-
arch/x86/kernel/i387.c | 551 +++++++++++++++++++++++++++++++++++++++++++
arch/x86/kernel/i387_32.c | 551 -------------------------------------------
3 files changed, 553 insertions(+), 552 deletions(-)
diff --git a/arch/x86/kernel/Makefile_32 b/arch/x86/kernel/Makefile_32
index c7a959d..a671980 100644
--- a/arch/x86/kernel/Makefile_32
+++ b/arch/x86/kernel/Makefile_32
@@ -7,9 +7,10 @@ CPPFLAGS_vmlinux.lds += -Ui386
obj-y := process_32.o signal_32.o entry_32.o traps_32.o irq_32.o \
time_32.o ioport_32.o ldt.o setup_32.o i8259_32.o sys_i386_32.o \
- pci-dma_32.o i386_ksyms_32.o i387_32.o bootflag.o e820_32.o\
+ pci-dma_32.o i386_ksyms_32.o bootflag.o e820_32.o\
quirks.o i8237.o topology.o alternative.o i8253.o tsc_32.o io_delay.o rtc.o
+obj-y += i387.o
obj-y += ptrace.o
obj-y += ds.o
obj-y += tls.o
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
new file mode 100644
index 0000000..bebe034
--- /dev/null
+++ b/arch/x86/kernel/i387.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 1994 Linus Torvalds
+ *
+ * Pentium III FXSR, SSE support
+ * General FPU state handling cleanups
+ * Gareth Hughes <[email protected]>, May 2000
+ */
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <asm/processor.h>
+#include <asm/i387.h>
+#include <asm/math_emu.h>
+#include <asm/sigcontext.h>
+#include <asm/user.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_MATH_EMULATION
+#define HAVE_HWFP (boot_cpu_data.hard_math)
+#else
+#define HAVE_HWFP 1
+#endif
+
+static unsigned long mxcsr_feature_mask __read_mostly = 0xffffffff;
+
+void mxcsr_feature_mask_init(void)
+{
+ unsigned long mask = 0;
+ clts();
+ if (cpu_has_fxsr) {
+ memset(¤t->thread.i387.fxsave, 0,
+ sizeof(struct i387_fxsave_struct));
+ asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave));
+ mask = current->thread.i387.fxsave.mxcsr_mask;
+ if (mask == 0)
+ mask = 0x0000ffbf;
+ }
+ mxcsr_feature_mask &= mask;
+ stts();
+}
+
+/*
+ * The _current_ task is using the FPU for the first time
+ * so initialize it and set the mxcsr to its default
+ * value at reset if we support XMM instructions and then
+ * remeber the current task has used the FPU.
+ */
+void init_fpu(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ memset(&tsk->thread.i387.fxsave, 0,
+ sizeof(struct i387_fxsave_struct));
+ tsk->thread.i387.fxsave.cwd = 0x37f;
+ if (cpu_has_xmm)
+ tsk->thread.i387.fxsave.mxcsr = 0x1f80;
+ } else {
+ memset(&tsk->thread.i387.fsave, 0,
+ sizeof(struct i387_fsave_struct));
+ tsk->thread.i387.fsave.cwd = 0xffff037fu;
+ tsk->thread.i387.fsave.swd = 0xffff0000u;
+ tsk->thread.i387.fsave.twd = 0xffffffffu;
+ tsk->thread.i387.fsave.fos = 0xffff0000u;
+ }
+ /* only the device not available exception
+ * or ptrace can call init_fpu */
+ set_stopped_child_used_math(tsk);
+}
+
+/*
+ * FPU lazy state save handling.
+ */
+
+void kernel_fpu_begin(void)
+{
+ struct thread_info *thread = current_thread_info();
+
+ preempt_disable();
+ if (thread->status & TS_USEDFPU) {
+ __save_init_fpu(thread->task);
+ return;
+ }
+ clts();
+}
+EXPORT_SYMBOL_GPL(kernel_fpu_begin);
+
+/*
+ * FPU tag word conversions.
+ */
+
+static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
+{
+ unsigned int tmp; /* to avoid 16 bit prefixes in the code */
+
+ /* Transform each pair of bits into 01 (valid) or 00 (empty) */
+ tmp = ~twd;
+ tmp = (tmp | (tmp >> 1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
+ /* and move the valid bits to the lower byte. */
+ tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
+ tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
+ tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
+
+ return tmp;
+}
+
+static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
+{
+ struct _fpxreg *st = NULL;
+ unsigned long tos = (fxsave->swd >> 11) & 7;
+ unsigned long twd = (unsigned long) fxsave->twd;
+ unsigned long tag;
+ unsigned long ret = 0xffff0000u;
+ int i;
+
+#define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
+
+ for (i = 0; i < 8; i++) {
+ if (twd & 0x1) {
+ st = FPREG_ADDR(fxsave, (i - tos) & 7);
+
+ switch (st->exponent & 0x7fff) {
+ case 0x7fff:
+ tag = 2; /* Special */
+ break;
+ case 0x0000:
+ if (!st->significand[0] &&
+ !st->significand[1] &&
+ !st->significand[2] &&
+ !st->significand[3]) {
+ tag = 1; /* Zero */
+ } else {
+ tag = 2; /* Special */
+ }
+ break;
+ default:
+ if (st->significand[3] & 0x8000) {
+ tag = 0; /* Valid */
+ } else {
+ tag = 2; /* Special */
+ }
+ break;
+ }
+ } else {
+ tag = 3; /* Empty */
+ }
+ ret |= (tag << (2 * i));
+ twd = twd >> 1;
+ }
+ return ret;
+}
+
+/*
+ * FPU state interaction.
+ */
+
+unsigned short get_fpu_cwd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.cwd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.cwd;
+ }
+}
+
+unsigned short get_fpu_swd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.swd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.swd;
+ }
+}
+
+#if 0
+unsigned short get_fpu_twd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.twd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.twd;
+ }
+}
+#endif /* 0 */
+
+unsigned short get_fpu_mxcsr(struct task_struct *tsk)
+{
+ if (cpu_has_xmm) {
+ return tsk->thread.i387.fxsave.mxcsr;
+ } else {
+ return 0x1f80;
+ }
+}
+
+#if 0
+
+void set_fpu_cwd(struct task_struct *tsk, unsigned short cwd)
+{
+ if (cpu_has_fxsr) {
+ tsk->thread.i387.fxsave.cwd = cwd;
+ } else {
+ tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000u);
+ }
+}
+
+void set_fpu_swd(struct task_struct *tsk, unsigned short swd)
+{
+ if (cpu_has_fxsr) {
+ tsk->thread.i387.fxsave.swd = swd;
+ } else {
+ tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000u);
+ }
+}
+
+void set_fpu_twd(struct task_struct *tsk, unsigned short twd)
+{
+ if (cpu_has_fxsr) {
+ tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
+ } else {
+ tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000u);
+ }
+}
+
+#endif /* 0 */
+
+/*
+ * FXSR floating point environment conversions.
+ */
+
+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;
+
+ env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
+ env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
+ env[2] = twd_fxsr_to_i387(fxsave);
+ env[3] = fxsave->fip;
+ env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
+ env[5] = fxsave->foo;
+ env[6] = fxsave->fos;
+
+ if (__copy_to_user(buf, env, 7 * sizeof(unsigned long)))
+ return 1;
+
+ to = &buf->_st[0];
+ from = (struct _fpxreg *) &fxsave->st_space[0];
+ for (i = 0; i < 8; i++, to++, from++) {
+ unsigned long __user *t = (unsigned long __user *)to;
+ unsigned long *f = (unsigned long *)from;
+
+ if (__put_user(*f, t) ||
+ __put_user(*(f + 1), t + 1) ||
+ __put_user(from->exponent, &to->exponent))
+ return 1;
+ }
+ return 0;
+}
+
+static int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
+ struct _fpstate __user *buf)
+{
+ unsigned long env[7];
+ struct _fpxreg *to;
+ struct _fpreg __user *from;
+ int i;
+
+ 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];
+
+ to = (struct _fpxreg *) &fxsave->st_space[0];
+ from = &buf->_st[0];
+ for (i = 0; i < 8; i++, to++, from++) {
+ unsigned long *t = (unsigned long *)to;
+ unsigned long __user *f = (unsigned long __user *)from;
+
+ if (__get_user(*t, f) ||
+ __get_user(*(t + 1), f + 1) ||
+ __get_user(to->exponent, &from->exponent))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Signal frame handlers.
+ */
+
+static inline int save_i387_fsave(struct _fpstate __user *buf)
+{
+ struct task_struct *tsk = current;
+
+ unlazy_fpu(tsk);
+ tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
+ if (__copy_to_user(buf, &tsk->thread.i387.fsave,
+ sizeof(struct i387_fsave_struct)))
+ return -1;
+ return 1;
+}
+
+static int save_i387_fxsave(struct _fpstate __user *buf)
+{
+ struct task_struct *tsk = current;
+ int err = 0;
+
+ unlazy_fpu(tsk);
+
+ if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave))
+ return -1;
+
+ err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
+ err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
+ if (err)
+ return -1;
+
+ if (__copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
+ sizeof(struct i387_fxsave_struct)))
+ return -1;
+ return 1;
+}
+
+int save_i387(struct _fpstate __user *buf)
+{
+ if (!used_math())
+ return 0;
+
+ /* This will cause a "finit" to be triggered by the next
+ * attempted FPU operation by the 'current' process.
+ */
+ clear_used_math();
+
+ if (HAVE_HWFP) {
+ if (cpu_has_fxsr) {
+ return save_i387_fxsave(buf);
+ } else {
+ return save_i387_fsave(buf);
+ }
+ } else {
+ return save_i387_soft(¤t->thread.i387.soft, buf);
+ }
+}
+
+static inline int restore_i387_fsave(struct _fpstate __user *buf)
+{
+ struct task_struct *tsk = current;
+ clear_fpu(tsk);
+ return __copy_from_user(&tsk->thread.i387.fsave, buf,
+ sizeof(struct i387_fsave_struct));
+}
+
+static int restore_i387_fxsave(struct _fpstate __user *buf)
+{
+ int err;
+ struct task_struct *tsk = current;
+ clear_fpu(tsk);
+ err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
+ sizeof(struct i387_fxsave_struct));
+ /* mxcsr reserved bits must be masked to zero for security reasons */
+ tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
+ return err ? 1 : convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf);
+}
+
+int restore_i387(struct _fpstate __user *buf)
+{
+ int err;
+
+ if (HAVE_HWFP) {
+ if (cpu_has_fxsr) {
+ err = restore_i387_fxsave(buf);
+ } else {
+ err = restore_i387_fsave(buf);
+ }
+ } else {
+ err = restore_i387_soft(¤t->thread.i387.soft, buf);
+ }
+ set_used_math();
+ return err;
+}
+
+/*
+ * ptrace request handlers.
+ */
+
+static inline int get_fpregs_fsave(struct user_i387_struct __user *buf,
+ struct task_struct *tsk)
+{
+ return __copy_to_user(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)
+{
+ return convert_fxsr_to_user((struct _fpstate __user *)buf,
+ &tsk->thread.i387.fxsave);
+}
+
+int get_fpregs(struct user_i387_struct __user *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);
+ }
+ } 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)
+{
+ return __copy_from_user(&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)
+{
+ return convert_fxsr_from_user(&tsk->thread.i387.fxsave,
+ (struct _fpstate __user *)buf);
+}
+
+int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
+{
+ if (HAVE_HWFP) {
+ if (cpu_has_fxsr) {
+ return set_fpregs_fxsave(tsk, buf);
+ } else {
+ return set_fpregs_fsave(tsk, buf);
+ }
+ } else {
+ return restore_i387_soft(&tsk->thread.i387.soft,
+ (struct _fpstate __user *)buf);
+ }
+}
+
+int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
+ sizeof(struct user_fxsr_struct)))
+ return -EFAULT;
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+int set_fpxregs(struct task_struct *tsk, struct user_fxsr_struct __user *buf)
+{
+ int ret = 0;
+
+ if (cpu_has_fxsr) {
+ if (__copy_from_user(&tsk->thread.i387.fxsave, buf,
+ sizeof(struct user_fxsr_struct)))
+ ret = -EFAULT;
+ /* mxcsr reserved bits must be masked to zero
+ * for security reasons */
+ tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
+ } else {
+ ret = -EIO;
+ }
+ return ret;
+}
+
+/*
+ * FPU state for core dumps.
+ */
+
+static inline void copy_fpu_fsave(struct task_struct *tsk,
+ struct user_i387_struct *fpu)
+{
+ memcpy(fpu, &tsk->thread.i387.fsave,
+ sizeof(struct user_i387_struct));
+}
+
+static inline void copy_fpu_fxsave(struct task_struct *tsk,
+ struct user_i387_struct *fpu)
+{
+ unsigned short *to;
+ unsigned short *from;
+ int i;
+
+ memcpy(fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long));
+
+ to = (unsigned short *)&fpu->st_space[0];
+ from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
+ for (i = 0; i < 8; i++, to += 5, from += 8)
+ memcpy(to, from, 5 * sizeof(unsigned short));
+}
+
+int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
+{
+ int fpvalid;
+ struct task_struct *tsk = current;
+
+ fpvalid = !!used_math();
+ if (fpvalid) {
+ unlazy_fpu(tsk);
+ if (cpu_has_fxsr) {
+ copy_fpu_fxsave(tsk, fpu);
+ } else {
+ copy_fpu_fsave(tsk, fpu);
+ }
+ }
+
+ return fpvalid;
+}
+EXPORT_SYMBOL(dump_fpu);
+
+int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
+{
+ int fpvalid = !!tsk_used_math(tsk);
+
+ if (fpvalid) {
+ if (tsk == current)
+ unlazy_fpu(tsk);
+ if (cpu_has_fxsr)
+ copy_fpu_fxsave(tsk, fpu);
+ else
+ copy_fpu_fsave(tsk, fpu);
+ }
+ return fpvalid;
+}
+
+int dump_task_extended_fpu(struct task_struct *tsk,
+ struct user_fxsr_struct *fpu)
+{
+ int fpvalid = tsk_used_math(tsk) && cpu_has_fxsr;
+
+ if (fpvalid) {
+ if (tsk == current)
+ unlazy_fpu(tsk);
+ memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(*fpu));
+ }
+ return fpvalid;
+}
diff --git a/arch/x86/kernel/i387_32.c b/arch/x86/kernel/i387_32.c
deleted file mode 100644
index bebe034..0000000
--- a/arch/x86/kernel/i387_32.c
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- *
- * Pentium III FXSR, SSE support
- * General FPU state handling cleanups
- * Gareth Hughes <[email protected]>, May 2000
- */
-
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/math_emu.h>
-#include <asm/sigcontext.h>
-#include <asm/user.h>
-#include <asm/ptrace.h>
-#include <asm/uaccess.h>
-
-#ifdef CONFIG_MATH_EMULATION
-#define HAVE_HWFP (boot_cpu_data.hard_math)
-#else
-#define HAVE_HWFP 1
-#endif
-
-static unsigned long mxcsr_feature_mask __read_mostly = 0xffffffff;
-
-void mxcsr_feature_mask_init(void)
-{
- unsigned long mask = 0;
- clts();
- if (cpu_has_fxsr) {
- memset(¤t->thread.i387.fxsave, 0,
- sizeof(struct i387_fxsave_struct));
- asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave));
- mask = current->thread.i387.fxsave.mxcsr_mask;
- if (mask == 0)
- mask = 0x0000ffbf;
- }
- mxcsr_feature_mask &= mask;
- stts();
-}
-
-/*
- * The _current_ task is using the FPU for the first time
- * so initialize it and set the mxcsr to its default
- * value at reset if we support XMM instructions and then
- * remeber the current task has used the FPU.
- */
-void init_fpu(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- memset(&tsk->thread.i387.fxsave, 0,
- sizeof(struct i387_fxsave_struct));
- tsk->thread.i387.fxsave.cwd = 0x37f;
- if (cpu_has_xmm)
- tsk->thread.i387.fxsave.mxcsr = 0x1f80;
- } else {
- memset(&tsk->thread.i387.fsave, 0,
- sizeof(struct i387_fsave_struct));
- tsk->thread.i387.fsave.cwd = 0xffff037fu;
- tsk->thread.i387.fsave.swd = 0xffff0000u;
- tsk->thread.i387.fsave.twd = 0xffffffffu;
- tsk->thread.i387.fsave.fos = 0xffff0000u;
- }
- /* only the device not available exception
- * or ptrace can call init_fpu */
- set_stopped_child_used_math(tsk);
-}
-
-/*
- * FPU lazy state save handling.
- */
-
-void kernel_fpu_begin(void)
-{
- struct thread_info *thread = current_thread_info();
-
- preempt_disable();
- if (thread->status & TS_USEDFPU) {
- __save_init_fpu(thread->task);
- return;
- }
- clts();
-}
-EXPORT_SYMBOL_GPL(kernel_fpu_begin);
-
-/*
- * FPU tag word conversions.
- */
-
-static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
-{
- unsigned int tmp; /* to avoid 16 bit prefixes in the code */
-
- /* Transform each pair of bits into 01 (valid) or 00 (empty) */
- tmp = ~twd;
- tmp = (tmp | (tmp >> 1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
- /* and move the valid bits to the lower byte. */
- tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
- tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
- tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
-
- return tmp;
-}
-
-static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
-{
- struct _fpxreg *st = NULL;
- unsigned long tos = (fxsave->swd >> 11) & 7;
- unsigned long twd = (unsigned long) fxsave->twd;
- unsigned long tag;
- unsigned long ret = 0xffff0000u;
- int i;
-
-#define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
-
- for (i = 0; i < 8; i++) {
- if (twd & 0x1) {
- st = FPREG_ADDR(fxsave, (i - tos) & 7);
-
- switch (st->exponent & 0x7fff) {
- case 0x7fff:
- tag = 2; /* Special */
- break;
- case 0x0000:
- if (!st->significand[0] &&
- !st->significand[1] &&
- !st->significand[2] &&
- !st->significand[3]) {
- tag = 1; /* Zero */
- } else {
- tag = 2; /* Special */
- }
- break;
- default:
- if (st->significand[3] & 0x8000) {
- tag = 0; /* Valid */
- } else {
- tag = 2; /* Special */
- }
- break;
- }
- } else {
- tag = 3; /* Empty */
- }
- ret |= (tag << (2 * i));
- twd = twd >> 1;
- }
- return ret;
-}
-
-/*
- * FPU state interaction.
- */
-
-unsigned short get_fpu_cwd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.cwd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.cwd;
- }
-}
-
-unsigned short get_fpu_swd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.swd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.swd;
- }
-}
-
-#if 0
-unsigned short get_fpu_twd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.twd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.twd;
- }
-}
-#endif /* 0 */
-
-unsigned short get_fpu_mxcsr(struct task_struct *tsk)
-{
- if (cpu_has_xmm) {
- return tsk->thread.i387.fxsave.mxcsr;
- } else {
- return 0x1f80;
- }
-}
-
-#if 0
-
-void set_fpu_cwd(struct task_struct *tsk, unsigned short cwd)
-{
- if (cpu_has_fxsr) {
- tsk->thread.i387.fxsave.cwd = cwd;
- } else {
- tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000u);
- }
-}
-
-void set_fpu_swd(struct task_struct *tsk, unsigned short swd)
-{
- if (cpu_has_fxsr) {
- tsk->thread.i387.fxsave.swd = swd;
- } else {
- tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000u);
- }
-}
-
-void set_fpu_twd(struct task_struct *tsk, unsigned short twd)
-{
- if (cpu_has_fxsr) {
- tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
- } else {
- tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000u);
- }
-}
-
-#endif /* 0 */
-
-/*
- * FXSR floating point environment conversions.
- */
-
-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;
-
- env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
- env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
- env[2] = twd_fxsr_to_i387(fxsave);
- env[3] = fxsave->fip;
- env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
- env[5] = fxsave->foo;
- env[6] = fxsave->fos;
-
- if (__copy_to_user(buf, env, 7 * sizeof(unsigned long)))
- return 1;
-
- to = &buf->_st[0];
- from = (struct _fpxreg *) &fxsave->st_space[0];
- for (i = 0; i < 8; i++, to++, from++) {
- unsigned long __user *t = (unsigned long __user *)to;
- unsigned long *f = (unsigned long *)from;
-
- if (__put_user(*f, t) ||
- __put_user(*(f + 1), t + 1) ||
- __put_user(from->exponent, &to->exponent))
- return 1;
- }
- return 0;
-}
-
-static int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
- struct _fpstate __user *buf)
-{
- unsigned long env[7];
- struct _fpxreg *to;
- struct _fpreg __user *from;
- int i;
-
- 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];
-
- to = (struct _fpxreg *) &fxsave->st_space[0];
- from = &buf->_st[0];
- for (i = 0; i < 8; i++, to++, from++) {
- unsigned long *t = (unsigned long *)to;
- unsigned long __user *f = (unsigned long __user *)from;
-
- if (__get_user(*t, f) ||
- __get_user(*(t + 1), f + 1) ||
- __get_user(to->exponent, &from->exponent))
- return 1;
- }
- return 0;
-}
-
-/*
- * Signal frame handlers.
- */
-
-static inline int save_i387_fsave(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
-
- unlazy_fpu(tsk);
- tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
- if (__copy_to_user(buf, &tsk->thread.i387.fsave,
- sizeof(struct i387_fsave_struct)))
- return -1;
- return 1;
-}
-
-static int save_i387_fxsave(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
- int err = 0;
-
- unlazy_fpu(tsk);
-
- if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave))
- return -1;
-
- err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
- err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
- if (err)
- return -1;
-
- if (__copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
- sizeof(struct i387_fxsave_struct)))
- return -1;
- return 1;
-}
-
-int save_i387(struct _fpstate __user *buf)
-{
- if (!used_math())
- return 0;
-
- /* This will cause a "finit" to be triggered by the next
- * attempted FPU operation by the 'current' process.
- */
- clear_used_math();
-
- if (HAVE_HWFP) {
- if (cpu_has_fxsr) {
- return save_i387_fxsave(buf);
- } else {
- return save_i387_fsave(buf);
- }
- } else {
- return save_i387_soft(¤t->thread.i387.soft, buf);
- }
-}
-
-static inline int restore_i387_fsave(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
- clear_fpu(tsk);
- return __copy_from_user(&tsk->thread.i387.fsave, buf,
- sizeof(struct i387_fsave_struct));
-}
-
-static int restore_i387_fxsave(struct _fpstate __user *buf)
-{
- int err;
- struct task_struct *tsk = current;
- clear_fpu(tsk);
- err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
- sizeof(struct i387_fxsave_struct));
- /* mxcsr reserved bits must be masked to zero for security reasons */
- tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- return err ? 1 : convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf);
-}
-
-int restore_i387(struct _fpstate __user *buf)
-{
- int err;
-
- if (HAVE_HWFP) {
- if (cpu_has_fxsr) {
- err = restore_i387_fxsave(buf);
- } else {
- err = restore_i387_fsave(buf);
- }
- } else {
- err = restore_i387_soft(¤t->thread.i387.soft, buf);
- }
- set_used_math();
- return err;
-}
-
-/*
- * ptrace request handlers.
- */
-
-static inline int get_fpregs_fsave(struct user_i387_struct __user *buf,
- struct task_struct *tsk)
-{
- return __copy_to_user(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)
-{
- return convert_fxsr_to_user((struct _fpstate __user *)buf,
- &tsk->thread.i387.fxsave);
-}
-
-int get_fpregs(struct user_i387_struct __user *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);
- }
- } 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)
-{
- return __copy_from_user(&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)
-{
- return convert_fxsr_from_user(&tsk->thread.i387.fxsave,
- (struct _fpstate __user *)buf);
-}
-
-int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
-{
- if (HAVE_HWFP) {
- if (cpu_has_fxsr) {
- return set_fpregs_fxsave(tsk, buf);
- } else {
- return set_fpregs_fsave(tsk, buf);
- }
- } else {
- return restore_i387_soft(&tsk->thread.i387.soft,
- (struct _fpstate __user *)buf);
- }
-}
-
-int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
- sizeof(struct user_fxsr_struct)))
- return -EFAULT;
- return 0;
- } else {
- return -EIO;
- }
-}
-
-int set_fpxregs(struct task_struct *tsk, struct user_fxsr_struct __user *buf)
-{
- int ret = 0;
-
- if (cpu_has_fxsr) {
- if (__copy_from_user(&tsk->thread.i387.fxsave, buf,
- sizeof(struct user_fxsr_struct)))
- ret = -EFAULT;
- /* mxcsr reserved bits must be masked to zero
- * for security reasons */
- tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- } else {
- ret = -EIO;
- }
- return ret;
-}
-
-/*
- * FPU state for core dumps.
- */
-
-static inline void copy_fpu_fsave(struct task_struct *tsk,
- struct user_i387_struct *fpu)
-{
- memcpy(fpu, &tsk->thread.i387.fsave,
- sizeof(struct user_i387_struct));
-}
-
-static inline void copy_fpu_fxsave(struct task_struct *tsk,
- struct user_i387_struct *fpu)
-{
- unsigned short *to;
- unsigned short *from;
- int i;
-
- memcpy(fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long));
-
- to = (unsigned short *)&fpu->st_space[0];
- from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
- for (i = 0; i < 8; i++, to += 5, from += 8)
- memcpy(to, from, 5 * sizeof(unsigned short));
-}
-
-int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
-{
- int fpvalid;
- struct task_struct *tsk = current;
-
- fpvalid = !!used_math();
- if (fpvalid) {
- unlazy_fpu(tsk);
- if (cpu_has_fxsr) {
- copy_fpu_fxsave(tsk, fpu);
- } else {
- copy_fpu_fsave(tsk, fpu);
- }
- }
-
- return fpvalid;
-}
-EXPORT_SYMBOL(dump_fpu);
-
-int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
-{
- int fpvalid = !!tsk_used_math(tsk);
-
- if (fpvalid) {
- if (tsk == current)
- unlazy_fpu(tsk);
- if (cpu_has_fxsr)
- copy_fpu_fxsave(tsk, fpu);
- else
- copy_fpu_fsave(tsk, fpu);
- }
- return fpvalid;
-}
-
-int dump_task_extended_fpu(struct task_struct *tsk,
- struct user_fxsr_struct *fpu)
-{
- int fpvalid = tsk_used_math(tsk) && cpu_has_fxsr;
-
- if (fpvalid) {
- if (tsk == current)
- unlazy_fpu(tsk);
- memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(*fpu));
- }
- return fpvalid;
-}
--
1.5.3.6
This cleans up the PTRACE_*REGS* request code so each one is just a
simple call to copy_regset_to_user or copy_regset_from_user. The
ptrace layouts already match the user_regset formats (core dump formats).
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/ptrace.c | 236 +++++++++++++++-------------------------------
1 files changed, 77 insertions(+), 159 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index ede27e9..e8c2ba3 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -750,9 +750,13 @@ void ptrace_disable(struct task_struct *child)
}
}
+#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
+static const struct user_regset_view user_x86_32_view; /* Initialized below. */
+#endif
+
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
- int i, ret;
+ int ret;
unsigned long __user *datap = (unsigned long __user *)data;
switch (request) {
@@ -805,82 +809,46 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
}
break;
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, datap, sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- for (i = 0; i < sizeof(struct user_regs_struct); i += sizeof(long)) {
- __put_user(getreg(child, i), datap);
- datap++;
- }
- ret = 0;
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- if (!access_ok(VERIFY_READ, datap, sizeof(struct user_regs_struct))) {
- ret = -EIO;
- break;
- }
- for (i = 0; i < sizeof(struct user_regs_struct); i += sizeof(long)) {
- __get_user(tmp, datap);
- putreg(child, i, tmp);
- datap++;
- }
- ret = 0;
- break;
- }
-
- case PTRACE_GETFPREGS: { /* Get the child FPU state. */
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- ret = 0;
- if (!tsk_used_math(child))
- init_fpu(child);
- get_fpregs((struct user_i387_struct __user *)data, child);
- break;
- }
-
- case PTRACE_SETFPREGS: { /* Set the child FPU state. */
- if (!access_ok(VERIFY_READ, datap,
- sizeof(struct user_i387_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- set_fpregs(child, (struct user_i387_struct __user *)data);
- ret = 0;
- break;
- }
+ case PTRACE_GETREGS: /* Get all gp regs from the child. */
+ return copy_regset_to_user(child,
+ task_user_regset_view(current),
+ REGSET_GENERAL,
+ 0, sizeof(struct user_regs_struct),
+ datap);
+
+ case PTRACE_SETREGS: /* Set all gp regs in the child. */
+ return copy_regset_from_user(child,
+ task_user_regset_view(current),
+ REGSET_GENERAL,
+ 0, sizeof(struct user_regs_struct),
+ datap);
+
+ case PTRACE_GETFPREGS: /* Get the child FPU state. */
+ return copy_regset_to_user(child,
+ task_user_regset_view(current),
+ REGSET_FP,
+ 0, sizeof(struct user_i387_struct),
+ datap);
+
+ case PTRACE_SETFPREGS: /* Set the child FPU state. */
+ return copy_regset_from_user(child,
+ task_user_regset_view(current),
+ REGSET_FP,
+ 0, sizeof(struct user_i387_struct),
+ datap);
#ifdef CONFIG_X86_32
- case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_fxsr_struct))) {
- ret = -EIO;
- break;
- }
- if (!tsk_used_math(child))
- init_fpu(child);
- ret = get_fpxregs((struct user_fxsr_struct __user *)data, child);
- break;
- }
-
- case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
- if (!access_ok(VERIFY_READ, datap,
- sizeof(struct user_fxsr_struct))) {
- ret = -EIO;
- break;
- }
- set_stopped_child_used_math(child);
- ret = set_fpxregs(child, (struct user_fxsr_struct __user *)data);
- break;
- }
+ case PTRACE_GETFPXREGS: /* Get the child extended FPU state. */
+ return copy_regset_to_user(child, &user_x86_32_view,
+ REGSET_XFP,
+ 0, sizeof(struct user_fxsr_struct),
+ datap);
+
+ case PTRACE_SETFPXREGS: /* Set the child extended FPU state. */
+ return copy_regset_from_user(child, &user_x86_32_view,
+ REGSET_XFP,
+ 0, sizeof(struct user_fxsr_struct),
+ datap);
#endif
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
@@ -1244,90 +1212,40 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
ret = putreg32(child, addr, data);
break;
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- int i;
-
- if (!access_ok(VERIFY_WRITE, datap, 16*4)) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (i = 0; i < sizeof(struct user_regs_struct32); i += sizeof(__u32)) {
- getreg32(child, i, &val);
- ret |= __put_user(val, (u32 __user *)datap);
- datap += sizeof(u32);
- }
- break;
- }
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- int i;
-
- if (!access_ok(VERIFY_READ, datap, 16*4)) {
- ret = -EIO;
- break;
- }
- ret = 0;
- for (i = 0; i < sizeof(struct user_regs_struct32); i += sizeof(u32)) {
- ret |= __get_user(tmp, (u32 __user *)datap);
- putreg32(child, i, tmp);
- datap += sizeof(u32);
- }
- break;
- }
-
- case PTRACE_GETFPREGS:
- ret = -EIO;
- if (!access_ok(VERIFY_READ, compat_ptr(data),
- sizeof(struct user_i387_struct)))
- break;
- save_i387_ia32(child, datap, childregs, 1);
- ret = 0;
- break;
-
- case PTRACE_SETFPREGS:
- ret = -EIO;
- if (!access_ok(VERIFY_WRITE, datap,
- sizeof(struct user_i387_struct)))
- break;
- ret = 0;
- /* don't check EFAULT to be bug-to-bug compatible to i386 */
- restore_i387_ia32(child, datap, 1);
- break;
-
- case PTRACE_GETFPXREGS: {
- struct user32_fxsr_struct __user *u = datap;
-
- init_fpu(child);
- ret = -EIO;
- if (!access_ok(VERIFY_WRITE, u, sizeof(*u)))
- break;
- ret = -EFAULT;
- if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u)))
- break;
- ret = __put_user(childregs->cs, &u->fcs);
- ret |= __put_user(child->thread.ds, &u->fos);
- break;
- }
- case PTRACE_SETFPXREGS: {
- struct user32_fxsr_struct __user *u = datap;
-
- unlazy_fpu(child);
- ret = -EIO;
- if (!access_ok(VERIFY_READ, u, sizeof(*u)))
- break;
- /*
- * no checking to be bug-to-bug compatible with i386.
- * but silence warning
- */
- if (__copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u)))
- ;
- set_stopped_child_used_math(child);
- child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- ret = 0;
- break;
- }
+ case PTRACE_GETREGS: /* Get all gp regs from the child. */
+ return copy_regset_to_user(child, &user_x86_32_view,
+ REGSET_GENERAL,
+ 0, sizeof(struct user_regs_struct32),
+ datap);
+
+ case PTRACE_SETREGS: /* Set all gp regs in the child. */
+ return copy_regset_from_user(child, &user_x86_32_view,
+ REGSET_GENERAL, 0,
+ sizeof(struct user_regs_struct32),
+ datap);
+
+ case PTRACE_GETFPREGS: /* Get the child FPU state. */
+ return copy_regset_to_user(child, &user_x86_32_view,
+ REGSET_FP, 0,
+ sizeof(struct user_i387_ia32_struct),
+ datap);
+
+ case PTRACE_SETFPREGS: /* Set the child FPU state. */
+ return copy_regset_from_user(
+ child, &user_x86_32_view, REGSET_FP,
+ 0, sizeof(struct user_i387_ia32_struct), datap);
+
+ case PTRACE_GETFPXREGS: /* Get the child extended FPU state. */
+ return copy_regset_to_user(child, &user_x86_32_view,
+ REGSET_XFP, 0,
+ sizeof(struct user32_fxsr_struct),
+ datap);
+
+ case PTRACE_SETFPXREGS: /* Set the child extended FPU state. */
+ return copy_regset_from_user(child, &user_x86_32_view,
+ REGSET_XFP, 0,
+ sizeof(struct user32_fxsr_struct),
+ datap);
case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message,
--
1.5.3.6
This implements user_regset-style accessors for the powerpc FPU data,
and rewrites the existing ptrace code in terms of those calls.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 37 +++++++++++++++++++++++++++++++------
1 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 8b056d2..f1ce646 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -21,6 +21,7 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/regset.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>
@@ -103,24 +104,48 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data)
}
+static int fpr_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ flush_fp_to_thread(target);
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) !=
+ offsetof(struct thread_struct, fpr[32]));
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpr, 0, -1);
+}
+
+static int fpr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ flush_fp_to_thread(target);
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) !=
+ offsetof(struct thread_struct, fpr[32]));
+
+ return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpr, 0, -1);
+}
+
static int get_fpregs(void __user *data, struct task_struct *task,
int has_fpscr)
{
unsigned int count = has_fpscr ? 33 : 32;
-
- if (copy_to_user(data, task->thread.fpr, count * sizeof(double)))
+ if (!access_ok(VERIFY_WRITE, data, count * sizeof(double)))
return -EFAULT;
- return 0;
+ return fpr_get(task, NULL, 0, count * sizeof(double), NULL, data);
}
static int set_fpregs(void __user *data, struct task_struct *task,
int has_fpscr)
{
unsigned int count = has_fpscr ? 33 : 32;
-
- if (copy_from_user(task->thread.fpr, data, count * sizeof(double)))
+ if (!access_ok(VERIFY_READ, data, count * sizeof(double)))
return -EFAULT;
- return 0;
+ return fpr_set(task, NULL, 0, count * sizeof(double), NULL, data);
}
--
1.5.3.6
This implements user_regset-style accessors for the powerpc SPE data,
and rewrites the existing ptrace code in terms of those calls.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 90 ++++++++++++++++++++++++++---------------
1 files changed, 57 insertions(+), 33 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 7cdf35a..8c25b00 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -272,55 +272,79 @@ static int set_vrregs(struct task_struct *task, unsigned long __user *data)
* }
*/
-/*
- * Get contents of SPE register state in task TASK.
- */
-static int get_evrregs(unsigned long *data, struct task_struct *task)
+static int evr_active(struct task_struct *target,
+ const struct user_regset *regset)
{
- int i;
+ flush_spe_to_thread(target);
+ return target->thread.used_spe ? regset->n : 0;
+}
- if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(unsigned long)))
- return -EFAULT;
+static int evr_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
- /* copy SPEFSCR */
- if (__put_user(task->thread.spefscr, &data[34]))
- return -EFAULT;
+ flush_spe_to_thread(target);
- /* copy SPE registers EVR[0] .. EVR[31] */
- for (i = 0; i < 32; i++, data++)
- if (__put_user(task->thread.evr[i], data))
- return -EFAULT;
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.evr,
+ 0, sizeof(target->thread.evr));
- /* copy ACC */
- if (__put_user64(task->thread.acc, (unsigned long long *)data))
- return -EFAULT;
+ BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) !=
+ offsetof(struct thread_struct, spefscr));
- return 0;
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.acc,
+ sizeof(target->thread.evr), -1);
+
+ return ret;
+}
+
+static int evr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ flush_spe_to_thread(target);
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.evr,
+ 0, sizeof(target->thread.evr));
+
+ BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) !=
+ offsetof(struct thread_struct, spefscr));
+
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.acc,
+ sizeof(target->thread.evr), -1);
+
+ return ret;
}
/*
- * Write contents of SPE register state into task TASK.
+ * Get contents of SPE register state in task TASK.
*/
-static int set_evrregs(struct task_struct *task, unsigned long *data)
+static int get_evrregs(unsigned long __user *data, struct task_struct *task)
{
- int i;
-
- if (!access_ok(VERIFY_READ, data, 35 * sizeof(unsigned long)))
+ if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(u32)))
return -EFAULT;
- /* copy SPEFSCR */
- if (__get_user(task->thread.spefscr, &data[34]))
- return -EFAULT;
+ return evr_get(task, NULL, 0, 35 * sizeof(u32), NULL, data);
+}
- /* copy SPE registers EVR[0] .. EVR[31] */
- for (i = 0; i < 32; i++, data++)
- if (__get_user(task->thread.evr[i], data))
- return -EFAULT;
- /* copy ACC */
- if (__get_user64(task->thread.acc, (unsigned long long*)data))
+/*
+ * Write contents of SPE register state into task TASK.
+ */
+static int set_evrregs(struct task_struct *task, unsigned long *data)
+{
+ if (!access_ok(VERIFY_READ, data, 35 * sizeof(u32)))
return -EFAULT;
- return 0;
+ return evr_set(task, NULL, 0, 35 * sizeof(u32), NULL, data);
}
#endif /* CONFIG_SPE */
--
1.5.3.6
This removes a bunch of dead code that is no longer needed now
that the user_regset interfaces are being used for all these jobs.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/i387.c | 105 ++++-------------------------------------
arch/x86/kernel/process_32.c | 16 ------
arch/x86/kernel/process_64.c | 19 -------
arch/x86/math-emu/fpu_entry.c | 14 ------
include/asm-x86/elf.h | 68 --------------------------
include/asm-x86/i387.h | 14 ------
include/asm-x86/math_emu.h | 5 --
7 files changed, 9 insertions(+), 232 deletions(-)
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
index f0563ed..7e354a3 100644
--- a/arch/x86/kernel/i387.c
+++ b/arch/x86/kernel/i387.c
@@ -454,113 +454,26 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
return err;
}
-#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
-
-#ifdef CONFIG_X86_64
-
-int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk)
-{
- return xfpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
-}
-
-int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
-{
- return xfpregs_set(tsk, NULL, 0, sizeof(*buf), NULL, buf);
-}
-
-#else
-
-int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk)
-{
- return fpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
-}
-
-int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
-{
- return fpregs_set(tsk, NULL, 0, sizeof(*buf), NULL, buf);
-}
-
-int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *tsk)
-{
- return xfpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
-}
-
-int set_fpxregs(struct task_struct *tsk, struct user_fxsr_struct __user *buf)
-{
- return xfpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
-}
-
-#endif
-
/*
* FPU state for core dumps.
+ * This is only used for a.out dumps now.
+ * It is declared generically using elf_fpregset_t (which is
+ * struct user_i387_struct) but is in fact only used for 32-bit
+ * dumps, so on 64-bit it is really struct user_i387_ia32_struct.
*/
-
-static inline void copy_fpu_fsave(struct task_struct *tsk,
- struct user_i387_struct *fpu)
-{
- memcpy(fpu, &tsk->thread.i387.fsave,
- sizeof(struct user_i387_struct));
-}
-
-static inline void copy_fpu_fxsave(struct task_struct *tsk,
- struct user_i387_struct *fpu)
-{
- unsigned short *to;
- unsigned short *from;
- int i;
-
- memcpy(fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long));
-
- to = (unsigned short *)&fpu->st_space[0];
- from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
- for (i = 0; i < 8; i++, to += 5, from += 8)
- memcpy(to, from, 5 * sizeof(unsigned short));
-}
-
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
{
int fpvalid;
struct task_struct *tsk = current;
fpvalid = !!used_math();
- if (fpvalid) {
- unlazy_fpu(tsk);
- if (cpu_has_fxsr) {
- copy_fpu_fxsave(tsk, fpu);
- } else {
- copy_fpu_fsave(tsk, fpu);
- }
- }
+ if (fpvalid)
+ fpvalid = !fpregs_get(tsk, NULL,
+ 0, sizeof(struct user_i387_ia32_struct),
+ fpu, NULL);
return fpvalid;
}
EXPORT_SYMBOL(dump_fpu);
-int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
-{
- int fpvalid = !!tsk_used_math(tsk);
-
- if (fpvalid) {
- if (tsk == current)
- unlazy_fpu(tsk);
- if (cpu_has_fxsr)
- copy_fpu_fxsave(tsk, fpu);
- else
- copy_fpu_fsave(tsk, fpu);
- }
- return fpvalid;
-}
-
-int dump_task_extended_fpu(struct task_struct *tsk,
- struct user32_fxsr_struct *fpu)
-{
- int fpvalid = tsk_used_math(tsk) && cpu_has_fxsr;
-
- if (fpvalid) {
- if (tsk == current)
- unlazy_fpu(tsk);
- memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(*fpu));
- }
- return fpvalid;
-}
+#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 1a77643..655902c 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -560,22 +560,6 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
}
EXPORT_SYMBOL(dump_thread);
-/*
- * Capture the user space registers if the task is not running (in user space)
- */
-int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
-{
- struct pt_regs ptregs = *task_pt_regs(tsk);
- ptregs.cs &= 0xffff;
- ptregs.ds &= 0xffff;
- ptregs.es &= 0xffff;
- ptregs.ss &= 0xffff;
-
- elf_core_copy_regs(regs, &ptregs);
-
- return 1;
-}
-
#ifdef CONFIG_SECCOMP
static void hard_disable_TSC(void)
{
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index c846108..c6ad1a0 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -533,24 +533,6 @@ out:
*/
#define loaddebug(thread, r) set_debugreg(thread->debugreg ## r, r)
-/*
- * Capture the user space registers if the task is not running (in user space)
- */
-int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
-{
- struct pt_regs *pp, ptregs;
-
- pp = task_pt_regs(tsk);
-
- ptregs = *pp;
- ptregs.cs &= 0xffff;
- ptregs.ss &= 0xffff;
-
- elf_core_copy_regs(regs, &ptregs);
-
- return 1;
-}
-
static inline void __switch_to_xtra(struct task_struct *prev_p,
struct task_struct *next_p,
struct tss_struct *tss)
@@ -918,4 +900,3 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
unsigned long range_end = mm->brk + 0x02000000;
return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
}
-
diff --git a/arch/x86/math-emu/fpu_entry.c b/arch/x86/math-emu/fpu_entry.c
index cfbdaa1..760baee 100644
--- a/arch/x86/math-emu/fpu_entry.c
+++ b/arch/x86/math-emu/fpu_entry.c
@@ -761,17 +761,3 @@ int fpregs_soft_get(struct task_struct *target,
return ret;
}
-
-int save_i387_soft(void *s387, struct _fpstate __user *buf)
-{
- return fpregs_soft_get(current, NULL,
- 0, sizeof(struct user_i387_struct),
- NULL, buf) ? -1 : 1;
-}
-
-int restore_i387_soft(void *s387, struct _fpstate __user *buf)
-{
- return fpregs_soft_set(current, NULL,
- 0, sizeof(struct user_i387_struct),
- NULL, buf) ? -1 : 1;
-}
diff --git a/include/asm-x86/elf.h b/include/asm-x86/elf.h
index 123e2d5..d6bf742 100644
--- a/include/asm-x86/elf.h
+++ b/include/asm-x86/elf.h
@@ -104,28 +104,6 @@ typedef struct user_fxsr_struct elf_fpxregset_t;
_r->ax = 0; \
} while (0)
-/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
- now struct_user_regs, they are different) */
-
-#define ELF_CORE_COPY_REGS(pr_reg, regs) \
- pr_reg[0] = regs->bx; \
- pr_reg[1] = regs->cx; \
- pr_reg[2] = regs->dx; \
- pr_reg[3] = regs->si; \
- pr_reg[4] = regs->di; \
- pr_reg[5] = regs->bp; \
- pr_reg[6] = regs->ax; \
- pr_reg[7] = regs->ds & 0xffff; \
- pr_reg[8] = regs->es & 0xffff; \
- pr_reg[9] = regs->fs & 0xffff; \
- savesegment(gs,pr_reg[10]); \
- pr_reg[11] = regs->orig_ax; \
- pr_reg[12] = regs->ip; \
- pr_reg[13] = regs->cs & 0xffff; \
- pr_reg[14] = regs->flags; \
- pr_reg[15] = regs->sp; \
- pr_reg[16] = regs->ss & 0xffff;
-
#define ELF_PLATFORM (utsname()->machine)
#define set_personality_64bit() do { } while (0)
extern unsigned int vdso_enabled;
@@ -159,41 +137,6 @@ extern unsigned int vdso_enabled;
clear_thread_flag(TIF_IA32); \
} while (0)
-/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
- now struct_user_regs, they are different). Assumes current is the process
- getting dumped. */
-
-#define ELF_CORE_COPY_REGS(pr_reg, regs) do { \
- unsigned v; \
- (pr_reg)[0] = (regs)->r15; \
- (pr_reg)[1] = (regs)->r14; \
- (pr_reg)[2] = (regs)->r13; \
- (pr_reg)[3] = (regs)->r12; \
- (pr_reg)[4] = (regs)->bp; \
- (pr_reg)[5] = (regs)->bx; \
- (pr_reg)[6] = (regs)->r11; \
- (pr_reg)[7] = (regs)->r10; \
- (pr_reg)[8] = (regs)->r9; \
- (pr_reg)[9] = (regs)->r8; \
- (pr_reg)[10] = (regs)->ax; \
- (pr_reg)[11] = (regs)->cx; \
- (pr_reg)[12] = (regs)->dx; \
- (pr_reg)[13] = (regs)->si; \
- (pr_reg)[14] = (regs)->di; \
- (pr_reg)[15] = (regs)->orig_ax; \
- (pr_reg)[16] = (regs)->ip; \
- (pr_reg)[17] = (regs)->cs; \
- (pr_reg)[18] = (regs)->flags; \
- (pr_reg)[19] = (regs)->sp; \
- (pr_reg)[20] = (regs)->ss; \
- (pr_reg)[21] = current->thread.fs; \
- (pr_reg)[22] = current->thread.gs; \
- asm("movl %%ds,%0" : "=r" (v)); (pr_reg)[23] = v; \
- asm("movl %%es,%0" : "=r" (v)); (pr_reg)[24] = v; \
- asm("movl %%fs,%0" : "=r" (v)); (pr_reg)[25] = v; \
- asm("movl %%gs,%0" : "=r" (v)); (pr_reg)[26] = v; \
-} while(0);
-
/* I'm not sure if we can use '-' here */
#define ELF_PLATFORM ("x86_64")
extern void set_personality_64bit(void);
@@ -236,18 +179,7 @@ extern int vdso_enabled;
struct task_struct;
-extern int dump_task_regs (struct task_struct *, elf_gregset_t *);
-extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *);
-
-#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs)
-#define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs)
-
#ifdef CONFIG_X86_32
-extern int dump_task_extended_fpu (struct task_struct *,
- struct user_fxsr_struct *);
-#define ELF_CORE_COPY_XFPREGS(tsk, elf_xfpregs) \
- dump_task_extended_fpu(tsk, elf_xfpregs)
-#define ELF_CORE_XFPREG_TYPE NT_PRXFPREG
#define VDSO_HIGH_BASE (__fix_to_virt(FIX_VDSO))
diff --git a/include/asm-x86/i387.h b/include/asm-x86/i387.h
index b52b891..3f4c4a5 100644
--- a/include/asm-x86/i387.h
+++ b/include/asm-x86/i387.h
@@ -322,20 +322,6 @@ static inline void clear_fpu(struct task_struct *tsk)
#endif /* CONFIG_X86_64 */
/*
- * 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);
-
-struct user_fxsr_struct;
-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);
-
-/*
* i387 state interaction
*/
static inline unsigned short get_fpu_cwd(struct task_struct *tsk)
diff --git a/include/asm-x86/math_emu.h b/include/asm-x86/math_emu.h
index a4b0aa3..9bf4ae9 100644
--- a/include/asm-x86/math_emu.h
+++ b/include/asm-x86/math_emu.h
@@ -1,11 +1,6 @@
#ifndef _I386_MATH_EMU_H
#define _I386_MATH_EMU_H
-#include <asm/sigcontext.h>
-
-int restore_i387_soft(void *s387, struct _fpstate __user *buf);
-int save_i387_soft(void *s387, struct _fpstate __user *buf);
-
/* This structure matches the layout of the data saved to the stack
following a device-not-present interrupt, part of it saved
automatically by the 80386/80486.
--
1.5.3.6
This removes all the old code that is no longer used after
the i387 unification and cleanup. The i387_64.h is renamed
to i387.h with no changes, but since it replaces the nonempty
one-line stub i387.h it looks like a big diff and not a rename.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/ia32/fpu32.c | 181 ----------------------
arch/x86/kernel/i387_64.c | 119 ---------------
include/asm-x86/i387.h | 369 ++++++++++++++++++++++++++++++++++++++++++++-
include/asm-x86/i387_32.h | 149 ------------------
include/asm-x86/i387_64.h | 368 --------------------------------------------
5 files changed, 368 insertions(+), 818 deletions(-)
diff --git a/arch/x86/ia32/fpu32.c b/arch/x86/ia32/fpu32.c
deleted file mode 100644
index ae80745..0000000
--- a/arch/x86/ia32/fpu32.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2002 Andi Kleen, SuSE Labs.
- * FXSAVE<->i387 conversion support. Based on code by Gareth Hughes.
- * This is used for ptrace, signals and coredumps in 32bit emulation.
- */
-
-#include <linux/sched.h>
-#include <asm/sigcontext32.h>
-#include <asm/processor.h>
-#include <asm/uaccess.h>
-#include <asm/i387.h>
-
-static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
-{
- unsigned int tmp; /* to avoid 16 bit prefixes in the code */
-
- /* Transform each pair of bits into 01 (valid) or 00 (empty) */
- tmp = ~twd;
- tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
- /* and move the valid bits to the lower byte. */
- tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
- tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
- tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
- return tmp;
-}
-
-#define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
-#define FP_EXP_TAG_VALID 0
-#define FP_EXP_TAG_ZERO 1
-#define FP_EXP_TAG_SPECIAL 2
-#define FP_EXP_TAG_EMPTY 3
-
-static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
-{
- struct _fpxreg *st;
- unsigned long tos = (fxsave->swd >> 11) & 7;
- unsigned long twd = (unsigned long) fxsave->twd;
- unsigned long tag;
- unsigned long ret = 0xffff0000;
- int i;
-
- for (i = 0; i < 8; i++, twd >>= 1) {
- if (twd & 0x1) {
- st = FPREG_ADDR(fxsave, (i - tos) & 7);
-
- switch (st->exponent & 0x7fff) {
- case 0x7fff:
- tag = FP_EXP_TAG_SPECIAL;
- break;
- case 0x0000:
- if (!st->significand[0] &&
- !st->significand[1] &&
- !st->significand[2] &&
- !st->significand[3])
- tag = FP_EXP_TAG_ZERO;
- else
- tag = FP_EXP_TAG_SPECIAL;
- break;
- default:
- if (st->significand[3] & 0x8000)
- tag = FP_EXP_TAG_VALID;
- else
- tag = FP_EXP_TAG_SPECIAL;
- break;
- }
- } else {
- tag = FP_EXP_TAG_EMPTY;
- }
- ret |= tag << (2 * i);
- }
- return ret;
-}
-
-#define G(num, val) err |= __get_user(val, num + (u32 __user *)buf)
-
-static inline int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
- struct _fpstate_ia32 __user *buf)
-{
- struct _fpxreg *to;
- struct _fpreg __user *from;
- int i, err = 0;
- u32 v;
-
- G(0, fxsave->cwd);
- G(1, fxsave->swd);
- G(2, fxsave->twd);
- fxsave->twd = twd_i387_to_fxsr(fxsave->twd);
- G(3, fxsave->rip);
- G(4, v);
- /* cs ignored */
- fxsave->fop = v>>16;
- G(5, fxsave->rdp);
- /* 6: ds ignored */
- if (err)
- return -1;
-
- to = (struct _fpxreg *)&fxsave->st_space[0];
- from = &buf->_st[0];
- for (i = 0; i < 8; i++, to++, from++) {
- if (__copy_from_user(to, from, sizeof(*from)))
- return -1;
- }
- return 0;
-}
-
-#define P(num, val) err |= __put_user(val, num + (u32 __user *)buf)
-
-static inline int convert_fxsr_to_user(struct _fpstate_ia32 __user *buf,
- struct i387_fxsave_struct *fxsave,
- struct pt_regs *regs,
- struct task_struct *tsk)
-{
- struct _fpreg __user *to;
- struct _fpxreg *from;
- int i, err = 0;
- u16 cs, ds;
-
- if (tsk == current) {
- /*
- * should be actually ds/cs at fpu exception time, but
- * that information is not available in 64bit mode.
- */
- asm("movw %%ds,%0 " : "=r" (ds));
- asm("movw %%cs,%0 " : "=r" (cs));
- } else {
- /* ptrace. task has stopped. */
- ds = tsk->thread.ds;
- cs = regs->cs;
- }
-
- P(0, (u32)fxsave->cwd | 0xffff0000);
- P(1, (u32)fxsave->swd | 0xffff0000);
- P(2, twd_fxsr_to_i387(fxsave));
- P(3, (u32)fxsave->rip);
- P(4, cs | ((u32)fxsave->fop) << 16);
- P(5, fxsave->rdp);
- P(6, 0xffff0000 | ds);
-
- if (err)
- return -1;
-
- to = &buf->_st[0];
- from = (struct _fpxreg *) &fxsave->st_space[0];
- for (i = 0; i < 8; i++, to++, from++) {
- if (__copy_to_user(to, from, sizeof(*to)))
- return -1;
- }
- return 0;
-}
-
-int restore_i387_ia32(struct task_struct *tsk,
- struct _fpstate_ia32 __user *buf, int fsave)
-{
- clear_fpu(tsk);
- if (!fsave) {
- if (__copy_from_user(&tsk->thread.i387.fxsave,
- &buf->_fxsr_env[0],
- sizeof(struct i387_fxsave_struct)))
- return -1;
- tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- set_stopped_child_used_math(tsk);
- }
- return convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf);
-}
-
-int save_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 __user *buf,
- struct pt_regs *regs, int fsave)
-{
- int err = 0;
-
- init_fpu(tsk);
- if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave, regs, tsk))
- return -1;
- if (fsave)
- return 0;
- err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
- err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
- err |= __copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
- sizeof(struct i387_fxsave_struct));
- return err ? -1 : 1;
-}
diff --git a/arch/x86/kernel/i387_64.c b/arch/x86/kernel/i387_64.c
deleted file mode 100644
index f335a76..0000000
--- a/arch/x86/kernel/i387_64.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- * Copyright (C) 2002 Andi Kleen, SuSE Labs
- *
- * Pentium III FXSR, SSE support
- * General FPU state handling cleanups
- * Gareth Hughes <[email protected]>, May 2000
- *
- * x86-64 rework 2002 Andi Kleen.
- * Does direct fxsave in and out of user space now for signal handlers.
- * All the FSAVE<->FXSAVE conversion code has been moved to the 32bit emulation,
- * the 64bit user space sees a FXSAVE frame directly.
- */
-
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/sigcontext.h>
-#include <asm/user.h>
-#include <asm/ptrace.h>
-#include <asm/uaccess.h>
-
-unsigned int mxcsr_feature_mask __read_mostly = 0xffffffff;
-
-void mxcsr_feature_mask_init(void)
-{
- unsigned int mask;
- clts();
- memset(¤t->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
- asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave));
- mask = current->thread.i387.fxsave.mxcsr_mask;
- if (mask == 0) mask = 0x0000ffbf;
- mxcsr_feature_mask &= mask;
- stts();
-}
-
-/*
- * Called at bootup to set up the initial FPU state that is later cloned
- * into all processes.
- */
-void __cpuinit fpu_init(void)
-{
- unsigned long oldcr0 = read_cr0();
- extern void __bad_fxsave_alignment(void);
-
- if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
- __bad_fxsave_alignment();
- set_in_cr4(X86_CR4_OSFXSR);
- set_in_cr4(X86_CR4_OSXMMEXCPT);
-
- write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */
-
- mxcsr_feature_mask_init();
- /* clean state in init */
- current_thread_info()->status = 0;
- clear_used_math();
-}
-
-void init_fpu(struct task_struct *child)
-{
- if (tsk_used_math(child)) {
- if (child == current)
- unlazy_fpu(child);
- return;
- }
- memset(&child->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
- child->thread.i387.fxsave.cwd = 0x37f;
- child->thread.i387.fxsave.mxcsr = 0x1f80;
- /* only the device not available exception or ptrace can call init_fpu */
- set_stopped_child_used_math(child);
-}
-
-/*
- * ptrace request handlers.
- */
-
-int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk)
-{
- init_fpu(tsk);
- return __copy_to_user(buf, &tsk->thread.i387.fxsave,
- sizeof(struct user_i387_struct)) ? -EFAULT : 0;
-}
-
-int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
-{
- if (__copy_from_user(&tsk->thread.i387.fxsave, buf,
- sizeof(struct user_i387_struct)))
- return -EFAULT;
- return 0;
-}
-
-/*
- * FPU state for core dumps.
- */
-
-int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
-{
- struct task_struct *tsk = current;
-
- if (!used_math())
- return 0;
-
- unlazy_fpu(tsk);
- memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct));
- return 1;
-}
-
-int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
-{
- int fpvalid = !!tsk_used_math(tsk);
-
- if (fpvalid) {
- if (tsk == current)
- unlazy_fpu(tsk);
- memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct));
-}
- return fpvalid;
-}
diff --git a/include/asm-x86/i387.h b/include/asm-x86/i387.h
index b2bc005..b52b891 100644
--- a/include/asm-x86/i387.h
+++ b/include/asm-x86/i387.h
@@ -1 +1,368 @@
-#include "i387_64.h"
+/*
+ * Copyright (C) 1994 Linus Torvalds
+ *
+ * Pentium III FXSR, SSE support
+ * General FPU state handling cleanups
+ * Gareth Hughes <[email protected]>, May 2000
+ * x86-64 work by Andi Kleen 2002
+ */
+
+#ifndef _ASM_X86_I387_H
+#define _ASM_X86_I387_H
+
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/regset.h>
+#include <asm/processor.h>
+#include <asm/sigcontext.h>
+#include <asm/user.h>
+#include <asm/uaccess.h>
+
+extern void fpu_init(void);
+extern unsigned int mxcsr_feature_mask;
+extern void mxcsr_feature_mask_init(void);
+extern void init_fpu(struct task_struct *child);
+extern asmlinkage void math_state_restore(void);
+
+extern user_regset_active_fn fpregs_active, xfpregs_active;
+extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get;
+extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
+
+#ifdef CONFIG_X86_64
+
+/* Ignore delayed exceptions from user space */
+static inline void tolerant_fwait(void)
+{
+ asm volatile("1: fwait\n"
+ "2:\n"
+ " .section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 1b,2b\n"
+ " .previous\n");
+}
+
+static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
+{
+ int err;
+
+ asm volatile("1: rex64/fxrstor (%[fx])\n\t"
+ "2:\n"
+ ".section .fixup,\"ax\"\n"
+ "3: movl $-1,%[err]\n"
+ " jmp 2b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 1b,3b\n"
+ ".previous"
+ : [err] "=r" (err)
+#if 0 /* See comment in __save_init_fpu() below. */
+ : [fx] "r" (fx), "m" (*fx), "0" (0));
+#else
+ : [fx] "cdaSDb" (fx), "m" (*fx), "0" (0));
+#endif
+ if (unlikely(err))
+ init_fpu(current);
+ return err;
+}
+
+#define X87_FSW_ES (1 << 7) /* Exception Summary */
+
+/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
+ is pending. Clear the x87 state here by setting it to fixed
+ values. The kernel data segment can be sometimes 0 and sometimes
+ new user value. Both should be ok.
+ Use the PDA as safe address because it should be already in L1. */
+static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
+{
+ if (unlikely(fx->swd & X87_FSW_ES))
+ asm volatile("fnclex");
+ alternative_input(ASM_NOP8 ASM_NOP2,
+ " emms\n" /* clear stack tags */
+ " fildl %%gs:0", /* load to clear state */
+ X86_FEATURE_FXSAVE_LEAK);
+}
+
+static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
+{
+ int err;
+
+ asm volatile("1: rex64/fxsave (%[fx])\n\t"
+ "2:\n"
+ ".section .fixup,\"ax\"\n"
+ "3: movl $-1,%[err]\n"
+ " jmp 2b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 1b,3b\n"
+ ".previous"
+ : [err] "=r" (err), "=m" (*fx)
+#if 0 /* See comment in __fxsave_clear() below. */
+ : [fx] "r" (fx), "0" (0));
+#else
+ : [fx] "cdaSDb" (fx), "0" (0));
+#endif
+ if (unlikely(err) && __clear_user(fx, sizeof(struct i387_fxsave_struct)))
+ err = -EFAULT;
+ /* No need to clear here because the caller clears USED_MATH */
+ return err;
+}
+
+static inline void __save_init_fpu(struct task_struct *tsk)
+{
+ /* Using "rex64; fxsave %0" is broken because, if the memory operand
+ uses any extended registers for addressing, a second REX prefix
+ will be generated (to the assembler, rex64 followed by semicolon
+ is a separate instruction), and hence the 64-bitness is lost. */
+#if 0
+ /* Using "fxsaveq %0" would be the ideal choice, but is only supported
+ starting with gas 2.16. */
+ __asm__ __volatile__("fxsaveq %0"
+ : "=m" (tsk->thread.i387.fxsave));
+#elif 0
+ /* Using, as a workaround, the properly prefixed form below isn't
+ accepted by any binutils version so far released, complaining that
+ the same type of prefix is used twice if an extended register is
+ needed for addressing (fix submitted to mainline 2005-11-21). */
+ __asm__ __volatile__("rex64/fxsave %0"
+ : "=m" (tsk->thread.i387.fxsave));
+#else
+ /* This, however, we can work around by forcing the compiler to select
+ an addressing mode that doesn't require extended registers. */
+ __asm__ __volatile__("rex64/fxsave %P2(%1)"
+ : "=m" (tsk->thread.i387.fxsave)
+ : "cdaSDb" (tsk),
+ "i" (offsetof(__typeof__(*tsk),
+ thread.i387.fxsave)));
+#endif
+ clear_fpu_state(&tsk->thread.i387.fxsave);
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+}
+
+/*
+ * Signal frame handlers.
+ */
+
+static inline int save_i387(struct _fpstate __user *buf)
+{
+ struct task_struct *tsk = current;
+ int err = 0;
+
+ BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
+ sizeof(tsk->thread.i387.fxsave));
+
+ if ((unsigned long)buf % 16)
+ printk("save_i387: bad fpstate %p\n", buf);
+
+ if (!used_math())
+ return 0;
+ clear_used_math(); /* trigger finit */
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ err = save_i387_checking((struct i387_fxsave_struct __user *)buf);
+ if (err) return err;
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+ stts();
+ } else {
+ if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
+ sizeof(struct i387_fxsave_struct)))
+ return -1;
+ }
+ return 1;
+}
+
+/*
+ * This restores directly out of user space. Exceptions are handled.
+ */
+static inline int restore_i387(struct _fpstate __user *buf)
+{
+ set_used_math();
+ if (!(task_thread_info(current)->status & TS_USEDFPU)) {
+ clts();
+ task_thread_info(current)->status |= TS_USEDFPU;
+ }
+ return restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
+}
+
+#else /* CONFIG_X86_32 */
+
+static inline void tolerant_fwait(void)
+{
+ asm volatile("fnclex ; fwait");
+}
+
+static inline void restore_fpu(struct task_struct *tsk)
+{
+ /*
+ * The "nop" is needed to make the instructions the same
+ * length.
+ */
+ alternative_input(
+ "nop ; frstor %1",
+ "fxrstor %1",
+ X86_FEATURE_FXSR,
+ "m" ((tsk)->thread.i387.fxsave));
+}
+
+/* We need a safe address that is cheap to find and that is already
+ in L1 during context switch. The best choices are unfortunately
+ different for UP and SMP */
+#ifdef CONFIG_SMP
+#define safe_address (__per_cpu_offset[0])
+#else
+#define safe_address (kstat_cpu(0).cpustat.user)
+#endif
+
+/*
+ * These must be called with preempt disabled
+ */
+static inline void __save_init_fpu(struct task_struct *tsk)
+{
+ /* Use more nops than strictly needed in case the compiler
+ varies code */
+ alternative_input(
+ "fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4,
+ "fxsave %[fx]\n"
+ "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:",
+ X86_FEATURE_FXSR,
+ [fx] "m" (tsk->thread.i387.fxsave),
+ [fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory");
+ /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
+ is pending. Clear the x87 state here by setting it to fixed
+ values. safe_address is a random variable that should be in L1 */
+ alternative_input(
+ GENERIC_NOP8 GENERIC_NOP2,
+ "emms\n\t" /* clear stack tags */
+ "fildl %[addr]", /* set F?P to defined value */
+ X86_FEATURE_FXSAVE_LEAK,
+ [addr] "m" (safe_address));
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+}
+
+/*
+ * Signal frame handlers...
+ */
+extern int save_i387(struct _fpstate __user *buf);
+extern int restore_i387(struct _fpstate __user *buf);
+
+#endif /* CONFIG_X86_64 */
+
+static inline void __unlazy_fpu(struct task_struct *tsk)
+{
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ __save_init_fpu(tsk);
+ stts();
+ } else
+ tsk->fpu_counter = 0;
+}
+
+static inline void __clear_fpu(struct task_struct *tsk)
+{
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ tolerant_fwait();
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+ stts();
+ }
+}
+
+static inline void kernel_fpu_begin(void)
+{
+ struct thread_info *me = current_thread_info();
+ preempt_disable();
+ if (me->status & TS_USEDFPU)
+ __save_init_fpu(me->task);
+ else
+ clts();
+}
+
+static inline void kernel_fpu_end(void)
+{
+ stts();
+ preempt_enable();
+}
+
+#ifdef CONFIG_X86_64
+
+static inline void save_init_fpu(struct task_struct *tsk)
+{
+ __save_init_fpu(tsk);
+ stts();
+}
+
+#define unlazy_fpu __unlazy_fpu
+#define clear_fpu __clear_fpu
+
+#else /* CONFIG_X86_32 */
+
+/*
+ * These disable preemption on their own and are safe
+ */
+static inline void save_init_fpu(struct task_struct *tsk)
+{
+ preempt_disable();
+ __save_init_fpu(tsk);
+ stts();
+ preempt_enable();
+}
+
+static inline void unlazy_fpu(struct task_struct *tsk)
+{
+ preempt_disable();
+ __unlazy_fpu(tsk);
+ preempt_enable();
+}
+
+static inline void clear_fpu(struct task_struct *tsk)
+{
+ preempt_disable();
+ __clear_fpu(tsk);
+ preempt_enable();
+}
+
+#endif /* CONFIG_X86_64 */
+
+/*
+ * 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);
+
+struct user_fxsr_struct;
+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);
+
+/*
+ * i387 state interaction
+ */
+static inline unsigned short get_fpu_cwd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.cwd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.cwd;
+ }
+}
+
+static inline unsigned short get_fpu_swd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.swd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.swd;
+ }
+}
+
+static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk)
+{
+ if (cpu_has_xmm) {
+ return tsk->thread.i387.fxsave.mxcsr;
+ } else {
+ return MXCSR_DEFAULT;
+ }
+}
+
+#endif /* _ASM_X86_I387_H */
diff --git a/include/asm-x86/i387_32.h b/include/asm-x86/i387_32.h
deleted file mode 100644
index 9ac2502..0000000
--- a/include/asm-x86/i387_32.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- *
- * Pentium III FXSR, SSE support
- * General FPU state handling cleanups
- * Gareth Hughes <[email protected]>, May 2000
- */
-
-#ifndef __ASM_I386_I387_H
-#define __ASM_I386_I387_H
-
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/kernel_stat.h>
-#include <asm/processor.h>
-#include <asm/sigcontext.h>
-#include <asm/user.h>
-
-extern void mxcsr_feature_mask_init(void);
-extern void init_fpu(struct task_struct *);
-
-/*
- * FPU lazy state save handling...
- */
-
-/*
- * The "nop" is needed to make the instructions the same
- * length.
- */
-#define restore_fpu(tsk) \
- alternative_input( \
- "nop ; frstor %1", \
- "fxrstor %1", \
- X86_FEATURE_FXSR, \
- "m" ((tsk)->thread.i387.fxsave))
-
-extern void kernel_fpu_begin(void);
-#define kernel_fpu_end() do { stts(); preempt_enable(); } while(0)
-
-/* We need a safe address that is cheap to find and that is already
- in L1 during context switch. The best choices are unfortunately
- different for UP and SMP */
-#ifdef CONFIG_SMP
-#define safe_address (__per_cpu_offset[0])
-#else
-#define safe_address (kstat_cpu(0).cpustat.user)
-#endif
-
-/*
- * These must be called with preempt disabled
- */
-static inline void __save_init_fpu( struct task_struct *tsk )
-{
- /* Use more nops than strictly needed in case the compiler
- varies code */
- alternative_input(
- "fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4,
- "fxsave %[fx]\n"
- "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:",
- X86_FEATURE_FXSR,
- [fx] "m" (tsk->thread.i387.fxsave),
- [fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory");
- /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
- is pending. Clear the x87 state here by setting it to fixed
- values. safe_address is a random variable that should be in L1 */
- alternative_input(
- GENERIC_NOP8 GENERIC_NOP2,
- "emms\n\t" /* clear stack tags */
- "fildl %[addr]", /* set F?P to defined value */
- X86_FEATURE_FXSAVE_LEAK,
- [addr] "m" (safe_address));
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
-}
-
-#define __unlazy_fpu( tsk ) do { \
- if (task_thread_info(tsk)->status & TS_USEDFPU) { \
- __save_init_fpu(tsk); \
- stts(); \
- } else \
- tsk->fpu_counter = 0; \
-} while (0)
-
-#define __clear_fpu( tsk ) \
-do { \
- if (task_thread_info(tsk)->status & TS_USEDFPU) { \
- asm volatile("fnclex ; fwait"); \
- task_thread_info(tsk)->status &= ~TS_USEDFPU; \
- stts(); \
- } \
-} while (0)
-
-
-/*
- * These disable preemption on their own and are safe
- */
-static inline void save_init_fpu( struct task_struct *tsk )
-{
- preempt_disable();
- __save_init_fpu(tsk);
- stts();
- preempt_enable();
-}
-
-#define unlazy_fpu( tsk ) do { \
- preempt_disable(); \
- __unlazy_fpu(tsk); \
- preempt_enable(); \
-} while (0)
-
-#define clear_fpu( tsk ) do { \
- preempt_disable(); \
- __clear_fpu( tsk ); \
- preempt_enable(); \
-} while (0)
-
-/*
- * FPU state interaction...
- */
-extern unsigned short get_fpu_cwd( struct task_struct *tsk );
-extern unsigned short get_fpu_swd( struct task_struct *tsk );
-extern unsigned short get_fpu_mxcsr( struct task_struct *tsk );
-extern asmlinkage void math_state_restore(void);
-
-/*
- * Signal frame handlers...
- */
-extern int save_i387( struct _fpstate __user *buf );
-extern int restore_i387( struct _fpstate __user *buf );
-
-/*
- * ptrace request handers...
- */
-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_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...
- */
-extern int dump_fpu( struct pt_regs *regs,
- struct user_i387_struct *fpu );
-
-#endif /* __ASM_I386_I387_H */
diff --git a/include/asm-x86/i387_64.h b/include/asm-x86/i387_64.h
deleted file mode 100644
index b52b891..0000000
--- a/include/asm-x86/i387_64.h
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- *
- * Pentium III FXSR, SSE support
- * General FPU state handling cleanups
- * Gareth Hughes <[email protected]>, May 2000
- * x86-64 work by Andi Kleen 2002
- */
-
-#ifndef _ASM_X86_I387_H
-#define _ASM_X86_I387_H
-
-#include <linux/sched.h>
-#include <linux/kernel_stat.h>
-#include <linux/regset.h>
-#include <asm/processor.h>
-#include <asm/sigcontext.h>
-#include <asm/user.h>
-#include <asm/uaccess.h>
-
-extern void fpu_init(void);
-extern unsigned int mxcsr_feature_mask;
-extern void mxcsr_feature_mask_init(void);
-extern void init_fpu(struct task_struct *child);
-extern asmlinkage void math_state_restore(void);
-
-extern user_regset_active_fn fpregs_active, xfpregs_active;
-extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get;
-extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
-
-#ifdef CONFIG_X86_64
-
-/* Ignore delayed exceptions from user space */
-static inline void tolerant_fwait(void)
-{
- asm volatile("1: fwait\n"
- "2:\n"
- " .section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 1b,2b\n"
- " .previous\n");
-}
-
-static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
-{
- int err;
-
- asm volatile("1: rex64/fxrstor (%[fx])\n\t"
- "2:\n"
- ".section .fixup,\"ax\"\n"
- "3: movl $-1,%[err]\n"
- " jmp 2b\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 1b,3b\n"
- ".previous"
- : [err] "=r" (err)
-#if 0 /* See comment in __save_init_fpu() below. */
- : [fx] "r" (fx), "m" (*fx), "0" (0));
-#else
- : [fx] "cdaSDb" (fx), "m" (*fx), "0" (0));
-#endif
- if (unlikely(err))
- init_fpu(current);
- return err;
-}
-
-#define X87_FSW_ES (1 << 7) /* Exception Summary */
-
-/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
- is pending. Clear the x87 state here by setting it to fixed
- values. The kernel data segment can be sometimes 0 and sometimes
- new user value. Both should be ok.
- Use the PDA as safe address because it should be already in L1. */
-static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
-{
- if (unlikely(fx->swd & X87_FSW_ES))
- asm volatile("fnclex");
- alternative_input(ASM_NOP8 ASM_NOP2,
- " emms\n" /* clear stack tags */
- " fildl %%gs:0", /* load to clear state */
- X86_FEATURE_FXSAVE_LEAK);
-}
-
-static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
-{
- int err;
-
- asm volatile("1: rex64/fxsave (%[fx])\n\t"
- "2:\n"
- ".section .fixup,\"ax\"\n"
- "3: movl $-1,%[err]\n"
- " jmp 2b\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 1b,3b\n"
- ".previous"
- : [err] "=r" (err), "=m" (*fx)
-#if 0 /* See comment in __fxsave_clear() below. */
- : [fx] "r" (fx), "0" (0));
-#else
- : [fx] "cdaSDb" (fx), "0" (0));
-#endif
- if (unlikely(err) && __clear_user(fx, sizeof(struct i387_fxsave_struct)))
- err = -EFAULT;
- /* No need to clear here because the caller clears USED_MATH */
- return err;
-}
-
-static inline void __save_init_fpu(struct task_struct *tsk)
-{
- /* Using "rex64; fxsave %0" is broken because, if the memory operand
- uses any extended registers for addressing, a second REX prefix
- will be generated (to the assembler, rex64 followed by semicolon
- is a separate instruction), and hence the 64-bitness is lost. */
-#if 0
- /* Using "fxsaveq %0" would be the ideal choice, but is only supported
- starting with gas 2.16. */
- __asm__ __volatile__("fxsaveq %0"
- : "=m" (tsk->thread.i387.fxsave));
-#elif 0
- /* Using, as a workaround, the properly prefixed form below isn't
- accepted by any binutils version so far released, complaining that
- the same type of prefix is used twice if an extended register is
- needed for addressing (fix submitted to mainline 2005-11-21). */
- __asm__ __volatile__("rex64/fxsave %0"
- : "=m" (tsk->thread.i387.fxsave));
-#else
- /* This, however, we can work around by forcing the compiler to select
- an addressing mode that doesn't require extended registers. */
- __asm__ __volatile__("rex64/fxsave %P2(%1)"
- : "=m" (tsk->thread.i387.fxsave)
- : "cdaSDb" (tsk),
- "i" (offsetof(__typeof__(*tsk),
- thread.i387.fxsave)));
-#endif
- clear_fpu_state(&tsk->thread.i387.fxsave);
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
-}
-
-/*
- * Signal frame handlers.
- */
-
-static inline int save_i387(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
- int err = 0;
-
- BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
- sizeof(tsk->thread.i387.fxsave));
-
- if ((unsigned long)buf % 16)
- printk("save_i387: bad fpstate %p\n", buf);
-
- if (!used_math())
- return 0;
- clear_used_math(); /* trigger finit */
- if (task_thread_info(tsk)->status & TS_USEDFPU) {
- err = save_i387_checking((struct i387_fxsave_struct __user *)buf);
- if (err) return err;
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
- stts();
- } else {
- if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
- sizeof(struct i387_fxsave_struct)))
- return -1;
- }
- return 1;
-}
-
-/*
- * This restores directly out of user space. Exceptions are handled.
- */
-static inline int restore_i387(struct _fpstate __user *buf)
-{
- set_used_math();
- if (!(task_thread_info(current)->status & TS_USEDFPU)) {
- clts();
- task_thread_info(current)->status |= TS_USEDFPU;
- }
- return restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
-}
-
-#else /* CONFIG_X86_32 */
-
-static inline void tolerant_fwait(void)
-{
- asm volatile("fnclex ; fwait");
-}
-
-static inline void restore_fpu(struct task_struct *tsk)
-{
- /*
- * The "nop" is needed to make the instructions the same
- * length.
- */
- alternative_input(
- "nop ; frstor %1",
- "fxrstor %1",
- X86_FEATURE_FXSR,
- "m" ((tsk)->thread.i387.fxsave));
-}
-
-/* We need a safe address that is cheap to find and that is already
- in L1 during context switch. The best choices are unfortunately
- different for UP and SMP */
-#ifdef CONFIG_SMP
-#define safe_address (__per_cpu_offset[0])
-#else
-#define safe_address (kstat_cpu(0).cpustat.user)
-#endif
-
-/*
- * These must be called with preempt disabled
- */
-static inline void __save_init_fpu(struct task_struct *tsk)
-{
- /* Use more nops than strictly needed in case the compiler
- varies code */
- alternative_input(
- "fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4,
- "fxsave %[fx]\n"
- "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:",
- X86_FEATURE_FXSR,
- [fx] "m" (tsk->thread.i387.fxsave),
- [fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory");
- /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
- is pending. Clear the x87 state here by setting it to fixed
- values. safe_address is a random variable that should be in L1 */
- alternative_input(
- GENERIC_NOP8 GENERIC_NOP2,
- "emms\n\t" /* clear stack tags */
- "fildl %[addr]", /* set F?P to defined value */
- X86_FEATURE_FXSAVE_LEAK,
- [addr] "m" (safe_address));
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
-}
-
-/*
- * Signal frame handlers...
- */
-extern int save_i387(struct _fpstate __user *buf);
-extern int restore_i387(struct _fpstate __user *buf);
-
-#endif /* CONFIG_X86_64 */
-
-static inline void __unlazy_fpu(struct task_struct *tsk)
-{
- if (task_thread_info(tsk)->status & TS_USEDFPU) {
- __save_init_fpu(tsk);
- stts();
- } else
- tsk->fpu_counter = 0;
-}
-
-static inline void __clear_fpu(struct task_struct *tsk)
-{
- if (task_thread_info(tsk)->status & TS_USEDFPU) {
- tolerant_fwait();
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
- stts();
- }
-}
-
-static inline void kernel_fpu_begin(void)
-{
- struct thread_info *me = current_thread_info();
- preempt_disable();
- if (me->status & TS_USEDFPU)
- __save_init_fpu(me->task);
- else
- clts();
-}
-
-static inline void kernel_fpu_end(void)
-{
- stts();
- preempt_enable();
-}
-
-#ifdef CONFIG_X86_64
-
-static inline void save_init_fpu(struct task_struct *tsk)
-{
- __save_init_fpu(tsk);
- stts();
-}
-
-#define unlazy_fpu __unlazy_fpu
-#define clear_fpu __clear_fpu
-
-#else /* CONFIG_X86_32 */
-
-/*
- * These disable preemption on their own and are safe
- */
-static inline void save_init_fpu(struct task_struct *tsk)
-{
- preempt_disable();
- __save_init_fpu(tsk);
- stts();
- preempt_enable();
-}
-
-static inline void unlazy_fpu(struct task_struct *tsk)
-{
- preempt_disable();
- __unlazy_fpu(tsk);
- preempt_enable();
-}
-
-static inline void clear_fpu(struct task_struct *tsk)
-{
- preempt_disable();
- __clear_fpu(tsk);
- preempt_enable();
-}
-
-#endif /* CONFIG_X86_64 */
-
-/*
- * 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);
-
-struct user_fxsr_struct;
-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);
-
-/*
- * i387 state interaction
- */
-static inline unsigned short get_fpu_cwd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.cwd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.cwd;
- }
-}
-
-static inline unsigned short get_fpu_swd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.swd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.swd;
- }
-}
-
-static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk)
-{
- if (cpu_has_xmm) {
- return tsk->thread.i387.fxsave.mxcsr;
- } else {
- return MXCSR_DEFAULT;
- }
-}
-
-#endif /* _ASM_X86_I387_H */
--
1.5.3.6
Remove the old ia32_binfmt.c file, which is no longer used.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/ia32/ia32_binfmt.c | 284 -------------------------------------------
1 files changed, 0 insertions(+), 284 deletions(-)
diff --git a/arch/x86/ia32/ia32_binfmt.c b/arch/x86/ia32/ia32_binfmt.c
deleted file mode 100644
index 806135c..0000000
--- a/arch/x86/ia32/ia32_binfmt.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Written 2000,2002 by Andi Kleen.
- *
- * Loosely based on the sparc64 and IA64 32bit emulation loaders.
- * This tricks binfmt_elf.c into loading 32bit binaries using lots
- * of ugly preprocessor tricks. Talk about very very poor man's inheritance.
- */
-
-#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/rwsem.h>
-#include <linux/sched.h>
-#include <linux/compat.h>
-#include <linux/string.h>
-#include <linux/binfmts.h>
-#include <linux/mm.h>
-#include <linux/security.h>
-#include <linux/elfcore-compat.h>
-
-#include <asm/segment.h>
-#include <asm/ptrace.h>
-#include <asm/processor.h>
-#include <asm/user32.h>
-#include <asm/sigcontext32.h>
-#include <asm/fpu32.h>
-#include <asm/i387.h>
-#include <asm/uaccess.h>
-#include <asm/ia32.h>
-#include <asm/vdso.h>
-
-#undef ELF_ARCH
-#undef ELF_CLASS
-#define ELF_CLASS ELFCLASS32
-#define ELF_ARCH EM_386
-
-#undef elfhdr
-#undef elf_phdr
-#undef elf_note
-#undef elf_addr_t
-#define elfhdr elf32_hdr
-#define elf_phdr elf32_phdr
-#define elf_note elf32_note
-#define elf_addr_t Elf32_Off
-
-#define ELF_NAME "elf/i386"
-
-#define AT_SYSINFO 32
-#define AT_SYSINFO_EHDR 33
-
-extern int sysctl_vsyscall32;
-
-#undef ARCH_DLINFO
-#define ARCH_DLINFO do { \
- if (sysctl_vsyscall32) { \
- NEW_AUX_ENT(AT_SYSINFO, (u32)VDSO_ENTRY); \
- NEW_AUX_ENT(AT_SYSINFO_EHDR, (u32)VDSO_CURRENT_BASE); \
- } \
-} while(0)
-
-struct file;
-
-#define IA32_EMULATOR 1
-
-#undef ELF_ET_DYN_BASE
-
-#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000)
-
-#define jiffies_to_timeval(a,b) do { (b)->tv_usec = 0; (b)->tv_sec = (a)/HZ; }while(0)
-
-#define _GET_SEG(x) \
- ({ __u32 seg; asm("movl %%" __stringify(x) ",%0" : "=r"(seg)); seg; })
-
-/* Assumes current==process to be dumped */
-#undef ELF_CORE_COPY_REGS
-#define ELF_CORE_COPY_REGS(pr_reg, regs) \
- pr_reg[0] = regs->bx; \
- pr_reg[1] = regs->cx; \
- pr_reg[2] = regs->dx; \
- pr_reg[3] = regs->si; \
- pr_reg[4] = regs->di; \
- pr_reg[5] = regs->bp; \
- pr_reg[6] = regs->ax; \
- pr_reg[7] = _GET_SEG(ds); \
- pr_reg[8] = _GET_SEG(es); \
- pr_reg[9] = _GET_SEG(fs); \
- pr_reg[10] = _GET_SEG(gs); \
- pr_reg[11] = regs->orig_ax; \
- pr_reg[12] = regs->ip; \
- pr_reg[13] = regs->cs; \
- pr_reg[14] = regs->flags; \
- pr_reg[15] = regs->sp; \
- pr_reg[16] = regs->ss;
-
-
-#define elf_prstatus compat_elf_prstatus
-#define elf_prpsinfo compat_elf_prpsinfo
-#define elf_fpregset_t struct user_i387_ia32_struct
-#define elf_fpxregset_t struct user32_fxsr_struct
-#define user user32
-
-#undef elf_read_implies_exec
-#define elf_read_implies_exec(ex, executable_stack) (executable_stack != EXSTACK_DISABLE_X)
-
-#define elf_core_copy_regs elf32_core_copy_regs
-static inline void elf32_core_copy_regs(compat_elf_gregset_t *elfregs,
- struct pt_regs *regs)
-{
- ELF_CORE_COPY_REGS((&elfregs->ebx), regs)
-}
-
-#define elf_core_copy_task_regs elf32_core_copy_task_regs
-static inline int elf32_core_copy_task_regs(struct task_struct *t,
- compat_elf_gregset_t* elfregs)
-{
- struct pt_regs *pp = task_pt_regs(t);
- ELF_CORE_COPY_REGS((&elfregs->ebx), pp);
- /* fix wrong segments */
- elfregs->ds = t->thread.ds;
- elfregs->fs = t->thread.fsindex;
- elfregs->gs = t->thread.gsindex;
- elfregs->es = t->thread.es;
- return 1;
-}
-
-#define elf_core_copy_task_fpregs elf32_core_copy_task_fpregs
-static inline int
-elf32_core_copy_task_fpregs(struct task_struct *tsk, struct pt_regs *regs,
- elf_fpregset_t *fpu)
-{
- struct _fpstate_ia32 *fpstate = (void*)fpu;
- mm_segment_t oldfs = get_fs();
-
- if (!tsk_used_math(tsk))
- return 0;
- if (!regs)
- regs = task_pt_regs(tsk);
- if (tsk == current)
- unlazy_fpu(tsk);
- set_fs(KERNEL_DS);
- save_i387_ia32(tsk, fpstate, regs, 1);
- /* Correct for i386 bug. It puts the fop into the upper 16bits of
- the tag word (like FXSAVE), not into the fcs*/
- fpstate->cssel |= fpstate->tag & 0xffff0000;
- set_fs(oldfs);
- return 1;
-}
-
-#define ELF_CORE_COPY_XFPREGS 1
-#define ELF_CORE_XFPREG_TYPE NT_PRXFPREG
-#define elf_core_copy_task_xfpregs elf32_core_copy_task_xfpregs
-static inline int
-elf32_core_copy_task_xfpregs(struct task_struct *t, elf_fpxregset_t *xfpu)
-{
- struct pt_regs *regs = task_pt_regs(t);
- if (!tsk_used_math(t))
- return 0;
- if (t == current)
- unlazy_fpu(t);
- memcpy(xfpu, &t->thread.i387.fxsave, sizeof(elf_fpxregset_t));
- xfpu->fcs = regs->cs;
- xfpu->fos = t->thread.ds; /* right? */
- return 1;
-}
-
-#undef elf_check_arch
-#define elf_check_arch(x) \
- ((x)->e_machine == EM_386)
-
-extern int force_personality32;
-
-#undef ELF_EXEC_PAGESIZE
-#undef ELF_HWCAP
-#undef ELF_PLATFORM
-#undef SET_PERSONALITY
-#define ELF_EXEC_PAGESIZE PAGE_SIZE
-#define ELF_HWCAP (boot_cpu_data.x86_capability[0])
-#define ELF_PLATFORM ("i686")
-#define SET_PERSONALITY(ex, ibcs2) \
-do { \
- unsigned long new_flags = 0; \
- if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
- new_flags = _TIF_IA32; \
- if ((current_thread_info()->flags & _TIF_IA32) \
- != new_flags) \
- set_thread_flag(TIF_ABI_PENDING); \
- else \
- clear_thread_flag(TIF_ABI_PENDING); \
- /* XXX This overwrites the user set personality */ \
- current->personality |= force_personality32; \
-} while (0)
-
-/* Override some function names */
-#define elf_format elf32_format
-
-#define init_elf_binfmt init_elf32_binfmt
-#define exit_elf_binfmt exit_elf32_binfmt
-
-#define load_elf_binary load_elf32_binary
-
-#undef ELF_PLAT_INIT
-#define ELF_PLAT_INIT(r, load_addr) elf32_init(r)
-
-#undef start_thread
-#define start_thread(regs,new_rip,new_rsp) do { \
- asm volatile("movl %0,%%fs" :: "r" (0)); \
- asm volatile("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); \
- load_gs_index(0); \
- (regs)->ip = (new_rip); \
- (regs)->sp = (new_rsp); \
- (regs)->flags = X86_EFLAGS_IF; \
- (regs)->cs = __USER32_CS; \
- (regs)->ss = __USER32_DS; \
- set_fs(USER_DS); \
-} while(0)
-
-
-#include <linux/module.h>
-
-MODULE_DESCRIPTION("Binary format loader for compatibility with IA32 ELF binaries.");
-MODULE_AUTHOR("Eric Youngdale, Andi Kleen");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
-static void elf32_init(struct pt_regs *);
-
-#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
-#define arch_setup_additional_pages syscall32_setup_pages
-extern int syscall32_setup_pages(struct linux_binprm *, int exstack);
-
-#include "../../../fs/binfmt_elf.c"
-
-static void elf32_init(struct pt_regs *regs)
-{
- struct task_struct *me = current;
- regs->di = 0;
- regs->si = 0;
- regs->dx = 0;
- regs->cx = 0;
- regs->ax = 0;
- regs->bx = 0;
- regs->bp = 0;
- regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 =
- regs->r13 = regs->r14 = regs->r15 = 0;
- me->thread.fs = 0;
- me->thread.gs = 0;
- me->thread.fsindex = 0;
- me->thread.gsindex = 0;
- me->thread.ds = __USER_DS;
- me->thread.es = __USER_DS;
-}
-
-#ifdef CONFIG_SYSCTL
-/* Register vsyscall32 into the ABI table */
-#include <linux/sysctl.h>
-
-static ctl_table abi_table2[] = {
- {
- .procname = "vsyscall32",
- .data = &sysctl_vsyscall32,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
- {}
-};
-
-static ctl_table abi_root_table2[] = {
- {
- .ctl_name = CTL_ABI,
- .procname = "abi",
- .mode = 0555,
- .child = abi_table2
- },
- {}
-};
-
-static __init int ia32_binfmt_init(void)
-{
- register_sysctl_table(abi_root_table2);
- return 0;
-}
-__initcall(ia32_binfmt_init);
-#endif
--
1.5.3.6
This switches x86-64's 32-bit ELF support to use the shared
fs/compat_binfmt_elf.c code instead of our own ia32_binfmt.c.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/ia32/Makefile | 5 +-
arch/x86/vdso/vdso32-setup.c | 33 ++++++++++++++++
include/asm-x86/elf.h | 88 ++++++++++++++++++++++++++++++-----------
3 files changed, 100 insertions(+), 26 deletions(-)
diff --git a/arch/x86/ia32/Makefile b/arch/x86/ia32/Makefile
index 93a6fda..369ae27 100644
--- a/arch/x86/ia32/Makefile
+++ b/arch/x86/ia32/Makefile
@@ -2,8 +2,7 @@
# Makefile for the ia32 kernel emulation subsystem.
#
-obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o \
- ia32_binfmt.o
+obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o
sysv-$(CONFIG_SYSVIPC) := ipc32.o
obj-$(CONFIG_IA32_EMULATION) += $(sysv-y)
@@ -12,3 +11,5 @@ obj-$(CONFIG_IA32_AOUT) += ia32_aout.o
audit-class-$(CONFIG_AUDIT) := audit.o
obj-$(CONFIG_IA32_EMULATION) += $(audit-class-y)
+
+obj-$(CONFIG_IA32_EMULATION) += ../../../fs/compat_binfmt_elf.o
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
index e0feb66..348f134 100644
--- a/arch/x86/vdso/vdso32-setup.c
+++ b/arch/x86/vdso/vdso32-setup.c
@@ -377,6 +377,39 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
__initcall(sysenter_setup);
+#ifdef CONFIG_SYSCTL
+/* Register vsyscall32 into the ABI table */
+#include <linux/sysctl.h>
+
+static ctl_table abi_table2[] = {
+ {
+ .procname = "vsyscall32",
+ .data = &sysctl_vsyscall32,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {}
+};
+
+static ctl_table abi_root_table2[] = {
+ {
+ .ctl_name = CTL_ABI,
+ .procname = "abi",
+ .mode = 0555,
+ .child = abi_table2
+ },
+ {}
+};
+
+static __init int ia32_binfmt_init(void)
+{
+ register_sysctl_table(abi_root_table2);
+ return 0;
+}
+__initcall(ia32_binfmt_init);
+#endif
+
#else /* CONFIG_X86_32 */
const char *arch_vma_name(struct vm_area_struct *vma)
diff --git a/include/asm-x86/elf.h b/include/asm-x86/elf.h
index d6bf742..dab4744 100644
--- a/include/asm-x86/elf.h
+++ b/include/asm-x86/elf.h
@@ -73,6 +73,9 @@ typedef struct user_fxsr_struct elf_fpxregset_t;
#endif
#ifdef __KERNEL__
+#include <asm/vdso.h>
+
+extern unsigned int vdso_enabled;
/*
* This is used to ensure we don't load something for the wrong architecture.
@@ -84,7 +87,6 @@ typedef struct user_fxsr_struct elf_fpxregset_t;
#include <asm/processor.h>
#include <asm/system.h> /* for savesegment */
#include <asm/desc.h>
-#include <asm/vdso.h>
#define elf_check_arch(x) elf_check_arch_ia32(x)
@@ -106,7 +108,6 @@ typedef struct user_fxsr_struct elf_fpxregset_t;
#define ELF_PLATFORM (utsname()->machine)
#define set_personality_64bit() do { } while (0)
-extern unsigned int vdso_enabled;
#else /* CONFIG_X86_32 */
@@ -118,29 +119,57 @@ extern unsigned int vdso_enabled;
#define elf_check_arch(x) \
((x)->e_machine == EM_X86_64)
+#define compat_elf_check_arch(x) elf_check_arch_ia32(x)
+
+static inline void start_ia32_thread(struct pt_regs *regs, u32 ip, u32 sp)
+{
+ asm volatile("movl %0,%%fs" :: "r" (0));
+ asm volatile("movl %0,%%es; movl %0,%%ds" : : "r" (__USER32_DS));
+ load_gs_index(0);
+ regs->ip = ip;
+ regs->sp = sp;
+ regs->flags = X86_EFLAGS_IF;
+ regs->cs = __USER32_CS;
+ regs->ss = __USER32_DS;
+}
+
+static inline void elf_common_init(struct thread_struct *t,
+ struct pt_regs *regs, const u16 ds)
+{
+ regs->ax = regs->bx = regs->cx = regs->dx = 0;
+ regs->si = regs->di = regs->bp = 0;
+ regs->r8 = regs->r9 = regs->r10 = regs->r11 = 0;
+ regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0;
+ t->fs = t->gs = 0;
+ t->fsindex = t->gsindex = 0;
+ t->ds = t->es = ds;
+}
+
#define ELF_PLAT_INIT(_r, load_addr) do { \
- struct task_struct *cur = current; \
- (_r)->bx = 0; (_r)->cx = 0; (_r)->dx = 0; \
- (_r)->si = 0; (_r)->di = 0; (_r)->bp = 0; \
- (_r)->ax = 0; \
- (_r)->r8 = 0; \
- (_r)->r9 = 0; \
- (_r)->r10 = 0; \
- (_r)->r11 = 0; \
- (_r)->r12 = 0; \
- (_r)->r13 = 0; \
- (_r)->r14 = 0; \
- (_r)->r15 = 0; \
- cur->thread.fs = 0; cur->thread.gs = 0; \
- cur->thread.fsindex = 0; cur->thread.gsindex = 0; \
- cur->thread.ds = 0; cur->thread.es = 0; \
+ elf_common_init(¤t->thread, _r, 0); \
clear_thread_flag(TIF_IA32); \
} while (0)
+#define COMPAT_ELF_PLAT_INIT(regs, load_addr) \
+ elf_common_init(¤t->thread, regs, __USER_DS)
+#define compat_start_thread(regs, ip, sp) do { \
+ start_ia32_thread(regs, ip, sp); \
+ set_fs(USER_DS); \
+ } while (0)
+#define COMPAT_SET_PERSONALITY(ex, ibcs2) do { \
+ if (test_thread_flag(TIF_IA32)) \
+ clear_thread_flag(TIF_ABI_PENDING); \
+ else \
+ set_thread_flag(TIF_ABI_PENDING); \
+ current->personality |= force_personality32; \
+ } while (0)
+#define COMPAT_ELF_PLATFORM ("i686")
+
/* I'm not sure if we can use '-' here */
#define ELF_PLATFORM ("x86_64")
extern void set_personality_64bit(void);
-extern int vdso_enabled;
+extern unsigned int sysctl_vsyscall32;
+extern int force_personality32;
#endif /* !CONFIG_X86_32 */
@@ -179,17 +208,19 @@ extern int vdso_enabled;
struct task_struct;
+#define ARCH_DLINFO_IA32(vdso_enabled) \
+do if (vdso_enabled) { \
+ NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \
+ NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
+} while (0)
+
#ifdef CONFIG_X86_32
#define VDSO_HIGH_BASE (__fix_to_virt(FIX_VDSO))
-/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
+#define ARCH_DLINFO ARCH_DLINFO_IA32(vdso_enabled)
-#define ARCH_DLINFO \
-do if (vdso_enabled) { \
- NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \
- NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
-} while (0)
+/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
#else /* CONFIG_X86_32 */
@@ -203,6 +234,12 @@ do if (vdso_enabled) { \
NEW_AUX_ENT(AT_SYSINFO_EHDR,(unsigned long)current->mm->context.vdso);\
} while (0)
+#define AT_SYSINFO 32
+
+#define COMPAT_ARCH_DLINFO ARCH_DLINFO_IA32(sysctl_vsyscall32)
+
+#define COMPAT_ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000)
+
#endif /* !CONFIG_X86_32 */
#define VDSO_CURRENT_BASE ((unsigned long)current->mm->context.vdso)
@@ -216,6 +253,9 @@ struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int executable_stack);
+extern int syscall32_setup_pages(struct linux_binprm *, int exstack);
+#define compat_arch_setup_additional_pages syscall32_setup_pages
+
extern unsigned long arch_randomize_brk(struct mm_struct *mm);
#define arch_randomize_brk arch_randomize_brk
--
1.5.3.6
This revamps the i387 code to be shared across 32-bit, 64-bit,
and 32-on-64. It does so by consolidating the code in one place
based on the user_regset accessor interfaces. This switches
32-bit to using the i387_64.h header and 64-bit to using the
i387.c that was previously i387_32.c, but that's what took the
least cleanup in each file. Here i387.h is stubbed to always
include i387_64.h rather than renaming the file, to keep this
diff smaller and easier to read.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/ia32/Makefile | 2 +-
arch/x86/kernel/Makefile_64 | 3 +-
arch/x86/kernel/i387.c | 481 ++++++++++++++++++++++---------------------
include/asm-x86/i387.h | 6 +-
include/asm-x86/i387_64.h | 5 +
5 files changed, 257 insertions(+), 240 deletions(-)
diff --git a/arch/x86/ia32/Makefile b/arch/x86/ia32/Makefile
index ec71cfe..93a6fda 100644
--- a/arch/x86/ia32/Makefile
+++ b/arch/x86/ia32/Makefile
@@ -3,7 +3,7 @@
#
obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o \
- ia32_binfmt.o fpu32.o
+ ia32_binfmt.o
sysv-$(CONFIG_SYSVIPC) := ipc32.o
obj-$(CONFIG_IA32_EMULATION) += $(sysv-y)
diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64
index fbb3700..7fcf972 100644
--- a/arch/x86/kernel/Makefile_64
+++ b/arch/x86/kernel/Makefile_64
@@ -7,7 +7,7 @@ CPPFLAGS_vmlinux.lds += -Ux86_64
obj-y := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \
time_64.o ioport_64.o ldt.o setup_64.o i8259_64.o sys_x86_64.o \
- x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \
+ x8664_ksyms_64.o syscall_64.o vsyscall_64.o \
setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \
pci-dma_64.o pci-nommu_64.o alternative.o hpet.o tsc_64.o bugs_64.o \
i8253.o io_delay.o rtc.o
@@ -16,6 +16,7 @@ obj-y += ptrace.o
obj-y += ds.o
obj-y += step.o
+obj-y += i387.o
obj-$(CONFIG_IA32_EMULATION) += tls.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-y += cpu/
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
index bebe034..f0563ed 100644
--- a/arch/x86/kernel/i387.c
+++ b/arch/x86/kernel/i387.c
@@ -8,6 +8,7 @@
#include <linux/sched.h>
#include <linux/module.h>
+#include <linux/regset.h>
#include <asm/processor.h>
#include <asm/i387.h>
#include <asm/math_emu.h>
@@ -16,13 +17,29 @@
#include <asm/ptrace.h>
#include <asm/uaccess.h>
+#ifdef CONFIG_X86_64
+
+#include <asm/sigcontext32.h>
+#include <asm/user32.h>
+
+#else
+
+#define save_i387_ia32 save_i387
+#define restore_i387_ia32 restore_i387
+
+#define _fpstate_ia32 _fpstate
+#define user_i387_ia32_struct user_i387_struct
+#define user32_fxsr_struct user_fxsr_struct
+
+#endif
+
#ifdef CONFIG_MATH_EMULATION
#define HAVE_HWFP (boot_cpu_data.hard_math)
#else
#define HAVE_HWFP 1
#endif
-static unsigned long mxcsr_feature_mask __read_mostly = 0xffffffff;
+unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
void mxcsr_feature_mask_init(void)
{
@@ -40,6 +57,30 @@ void mxcsr_feature_mask_init(void)
stts();
}
+#ifdef CONFIG_X86_64
+/*
+ * Called at bootup to set up the initial FPU state that is later cloned
+ * into all processes.
+ */
+void __cpuinit fpu_init(void)
+{
+ unsigned long oldcr0 = read_cr0();
+ extern void __bad_fxsave_alignment(void);
+
+ if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
+ __bad_fxsave_alignment();
+ set_in_cr4(X86_CR4_OSFXSR);
+ set_in_cr4(X86_CR4_OSXMMEXCPT);
+
+ write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */
+
+ mxcsr_feature_mask_init();
+ /* clean state in init */
+ current_thread_info()->status = 0;
+ clear_used_math();
+}
+#endif /* CONFIG_X86_64 */
+
/*
* The _current_ task is using the FPU for the first time
* so initialize it and set the mxcsr to its default
@@ -48,12 +89,18 @@ void mxcsr_feature_mask_init(void)
*/
void init_fpu(struct task_struct *tsk)
{
+ if (tsk_used_math(tsk)) {
+ if (tsk == current)
+ unlazy_fpu(tsk);
+ return;
+ }
+
if (cpu_has_fxsr) {
memset(&tsk->thread.i387.fxsave, 0,
sizeof(struct i387_fxsave_struct));
tsk->thread.i387.fxsave.cwd = 0x37f;
if (cpu_has_xmm)
- tsk->thread.i387.fxsave.mxcsr = 0x1f80;
+ tsk->thread.i387.fxsave.mxcsr = MXCSR_DEFAULT;
} else {
memset(&tsk->thread.i387.fsave, 0,
sizeof(struct i387_fsave_struct));
@@ -62,27 +109,59 @@ void init_fpu(struct task_struct *tsk)
tsk->thread.i387.fsave.twd = 0xffffffffu;
tsk->thread.i387.fsave.fos = 0xffff0000u;
}
- /* only the device not available exception
- * or ptrace can call init_fpu */
+ /*
+ * Only the device not available exception or ptrace can call init_fpu.
+ */
set_stopped_child_used_math(tsk);
}
-/*
- * FPU lazy state save handling.
- */
+int fpregs_active(struct task_struct *target, const struct user_regset *regset)
+{
+ return tsk_used_math(target) ? regset->n : 0;
+}
-void kernel_fpu_begin(void)
+int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
{
- struct thread_info *thread = current_thread_info();
+ return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
+}
- preempt_disable();
- if (thread->status & TS_USEDFPU) {
- __save_init_fpu(thread->task);
- return;
- }
- clts();
+int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (!cpu_has_fxsr)
+ return -ENODEV;
+
+ unlazy_fpu(target);
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+}
+
+int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ if (!cpu_has_fxsr)
+ return -ENODEV;
+
+ unlazy_fpu(target);
+ set_stopped_child_used_math(target);
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+
+ /*
+ * mxcsr reserved bits must be masked to zero for security reasons.
+ */
+ target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(kernel_fpu_begin);
+
+#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
/*
* FPU tag word conversions.
@@ -94,210 +173,187 @@ static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
/* Transform each pair of bits into 01 (valid) or 00 (empty) */
tmp = ~twd;
- tmp = (tmp | (tmp >> 1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
+ tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
/* and move the valid bits to the lower byte. */
tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
-
return tmp;
}
-static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
-{
- struct _fpxreg *st = NULL;
- unsigned long tos = (fxsave->swd >> 11) & 7;
- unsigned long twd = (unsigned long) fxsave->twd;
- unsigned long tag;
- unsigned long ret = 0xffff0000u;
- int i;
-
#define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
+#define FP_EXP_TAG_VALID 0
+#define FP_EXP_TAG_ZERO 1
+#define FP_EXP_TAG_SPECIAL 2
+#define FP_EXP_TAG_EMPTY 3
+
+static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
+{
+ struct _fpxreg *st;
+ u32 tos = (fxsave->swd >> 11) & 7;
+ u32 twd = (unsigned long) fxsave->twd;
+ u32 tag;
+ u32 ret = 0xffff0000u;
+ int i;
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 8; i++, twd >>= 1) {
if (twd & 0x1) {
st = FPREG_ADDR(fxsave, (i - tos) & 7);
switch (st->exponent & 0x7fff) {
case 0x7fff:
- tag = 2; /* Special */
+ tag = FP_EXP_TAG_SPECIAL;
break;
case 0x0000:
if (!st->significand[0] &&
!st->significand[1] &&
!st->significand[2] &&
- !st->significand[3]) {
- tag = 1; /* Zero */
- } else {
- tag = 2; /* Special */
- }
+ !st->significand[3])
+ tag = FP_EXP_TAG_ZERO;
+ else
+ tag = FP_EXP_TAG_SPECIAL;
break;
default:
- if (st->significand[3] & 0x8000) {
- tag = 0; /* Valid */
- } else {
- tag = 2; /* Special */
- }
+ if (st->significand[3] & 0x8000)
+ tag = FP_EXP_TAG_VALID;
+ else
+ tag = FP_EXP_TAG_SPECIAL;
break;
}
} else {
- tag = 3; /* Empty */
+ tag = FP_EXP_TAG_EMPTY;
}
- ret |= (tag << (2 * i));
- twd = twd >> 1;
+ ret |= tag << (2 * i);
}
return ret;
}
/*
- * FPU state interaction.
+ * FXSR floating point environment conversions.
*/
-unsigned short get_fpu_cwd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.cwd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.cwd;
- }
-}
-
-unsigned short get_fpu_swd(struct task_struct *tsk)
+static void convert_from_fxsr(struct user_i387_ia32_struct *env,
+ struct task_struct *tsk)
{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.swd;
- } else {
- return (unsigned short)tsk->thread.i387.fsave.swd;
- }
-}
+ struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave;
+ struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
+ struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
+ int i;
-#if 0
-unsigned short get_fpu_twd(struct task_struct *tsk)
-{
- if (cpu_has_fxsr) {
- return tsk->thread.i387.fxsave.twd;
+ env->cwd = fxsave->cwd | 0xffff0000u;
+ env->swd = fxsave->swd | 0xffff0000u;
+ env->twd = twd_fxsr_to_i387(fxsave);
+
+#ifdef CONFIG_X86_64
+ env->fip = fxsave->rip;
+ env->foo = fxsave->rdp;
+ if (tsk == current) {
+ /*
+ * should be actually ds/cs at fpu exception time, but
+ * that information is not available in 64bit mode.
+ */
+ asm("mov %%ds,%0" : "=r" (env->fos));
+ asm("mov %%cs,%0" : "=r" (env->fcs));
} else {
- return (unsigned short)tsk->thread.i387.fsave.twd;
+ struct pt_regs *regs = task_pt_regs(tsk);
+ env->fos = 0xffff0000 | tsk->thread.ds;
+ env->fcs = regs->cs;
}
-}
-#endif /* 0 */
+#else
+ env->fip = fxsave->fip;
+ env->fcs = fxsave->fcs;
+ env->foo = fxsave->foo;
+ env->fos = fxsave->fos;
+#endif
-unsigned short get_fpu_mxcsr(struct task_struct *tsk)
-{
- if (cpu_has_xmm) {
- return tsk->thread.i387.fxsave.mxcsr;
- } else {
- return 0x1f80;
- }
+ for (i = 0; i < 8; ++i)
+ memcpy(&to[i], &from[i], sizeof(to[0]));
}
-#if 0
+static void convert_to_fxsr(struct task_struct *tsk,
+ const struct user_i387_ia32_struct *env)
-void set_fpu_cwd(struct task_struct *tsk, unsigned short cwd)
{
- if (cpu_has_fxsr) {
- tsk->thread.i387.fxsave.cwd = cwd;
- } else {
- tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000u);
- }
-}
+ struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave;
+ struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
+ struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
+ int i;
-void set_fpu_swd(struct task_struct *tsk, unsigned short swd)
-{
- if (cpu_has_fxsr) {
- tsk->thread.i387.fxsave.swd = swd;
- } else {
- tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000u);
- }
-}
+ fxsave->cwd = env->cwd;
+ fxsave->swd = env->swd;
+ fxsave->twd = twd_i387_to_fxsr(env->twd);
+ fxsave->fop = (u16) ((u32) env->fcs >> 16);
+#ifdef CONFIG_X86_64
+ fxsave->rip = env->fip;
+ fxsave->rdp = env->foo;
+ /* cs and ds ignored */
+#else
+ fxsave->fip = env->fip;
+ fxsave->fcs = (env->fcs & 0xffff);
+ fxsave->foo = env->foo;
+ fxsave->fos = env->fos;
+#endif
-void set_fpu_twd(struct task_struct *tsk, unsigned short twd)
-{
- if (cpu_has_fxsr) {
- tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
- } else {
- tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000u);
- }
+ for (i = 0; i < 8; ++i)
+ memcpy(&to[i], &from[i], sizeof(from[0]));
}
-#endif /* 0 */
-
-/*
- * FXSR floating point environment conversions.
- */
-
-static int convert_fxsr_to_user(struct _fpstate __user *buf,
- struct i387_fxsave_struct *fxsave)
+int fpregs_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- unsigned long env[7];
- struct _fpreg __user *to;
- struct _fpxreg *from;
- int i;
+ struct user_i387_ia32_struct env;
- env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
- env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
- env[2] = twd_fxsr_to_i387(fxsave);
- env[3] = fxsave->fip;
- env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
- env[5] = fxsave->foo;
- env[6] = fxsave->fos;
+ if (!HAVE_HWFP)
+ return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);
- if (__copy_to_user(buf, env, 7 * sizeof(unsigned long)))
- return 1;
+ unlazy_fpu(target);
- to = &buf->_st[0];
- from = (struct _fpxreg *) &fxsave->st_space[0];
- for (i = 0; i < 8; i++, to++, from++) {
- unsigned long __user *t = (unsigned long __user *)to;
- unsigned long *f = (unsigned long *)from;
+ if (!cpu_has_fxsr)
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fsave, 0, -1);
- if (__put_user(*f, t) ||
- __put_user(*(f + 1), t + 1) ||
- __put_user(from->exponent, &to->exponent))
- return 1;
+ if (kbuf && pos == 0 && count == sizeof(env)) {
+ convert_from_fxsr(kbuf, target);
+ return 0;
}
- return 0;
+
+ convert_from_fxsr(&env, target);
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
}
-static int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
- struct _fpstate __user *buf)
+int fpregs_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- unsigned long env[7];
- struct _fpxreg *to;
- struct _fpreg __user *from;
- int i;
+ int ret;
- if (__copy_from_user(env, buf, 7 * sizeof(long)))
- return 1;
+ if (!HAVE_HWFP)
+ return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
- 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];
-
- to = (struct _fpxreg *) &fxsave->st_space[0];
- from = &buf->_st[0];
- for (i = 0; i < 8; i++, to++, from++) {
- unsigned long *t = (unsigned long *)to;
- unsigned long __user *f = (unsigned long __user *)from;
-
- if (__get_user(*t, f) ||
- __get_user(*(t + 1), f + 1) ||
- __get_user(to->exponent, &from->exponent))
- return 1;
- }
- return 0;
+ unlazy_fpu(target);
+ set_stopped_child_used_math(target);
+
+ if (!cpu_has_fxsr)
+ return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fsave, 0, -1);
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.i387.fxsave, 0, -1);
+
+ /*
+ * mxcsr reserved bits must be masked to zero for security reasons.
+ */
+ target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
+
+ return ret;
}
/*
* Signal frame handlers.
*/
-static inline int save_i387_fsave(struct _fpstate __user *buf)
+static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
{
struct task_struct *tsk = current;
@@ -309,14 +365,16 @@ static inline int save_i387_fsave(struct _fpstate __user *buf)
return 1;
}
-static int save_i387_fxsave(struct _fpstate __user *buf)
+static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
{
struct task_struct *tsk = current;
+ struct user_i387_ia32_struct env;
int err = 0;
unlazy_fpu(tsk);
- if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave))
+ convert_from_fxsr(&env, tsk);
+ if (__copy_to_user(buf, &env, sizeof(env)))
return -1;
err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
@@ -330,7 +388,7 @@ static int save_i387_fxsave(struct _fpstate __user *buf)
return 1;
}
-int save_i387(struct _fpstate __user *buf)
+int save_i387_ia32(struct _fpstate_ia32 __user *buf)
{
if (!used_math())
return 0;
@@ -347,11 +405,13 @@ int save_i387(struct _fpstate __user *buf)
return save_i387_fsave(buf);
}
} else {
- return save_i387_soft(¤t->thread.i387.soft, buf);
+ return fpregs_soft_get(current, NULL,
+ 0, sizeof(struct user_i387_ia32_struct),
+ NULL, buf) ? -1 : 1;
}
}
-static inline int restore_i387_fsave(struct _fpstate __user *buf)
+static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
{
struct task_struct *tsk = current;
clear_fpu(tsk);
@@ -359,19 +419,23 @@ static inline int restore_i387_fsave(struct _fpstate __user *buf)
sizeof(struct i387_fsave_struct));
}
-static int restore_i387_fxsave(struct _fpstate __user *buf)
+static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
{
int err;
struct task_struct *tsk = current;
+ struct user_i387_ia32_struct env;
clear_fpu(tsk);
err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
sizeof(struct i387_fxsave_struct));
/* mxcsr reserved bits must be masked to zero for security reasons */
tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- return err ? 1 : convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf);
+ if (err || __copy_from_user(&env, buf, sizeof(env)))
+ return 1;
+ convert_to_fxsr(tsk, &env);
+ return 0;
}
-int restore_i387(struct _fpstate __user *buf)
+int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
{
int err;
@@ -382,101 +446,52 @@ int restore_i387(struct _fpstate __user *buf)
err = restore_i387_fsave(buf);
}
} else {
- err = restore_i387_soft(¤t->thread.i387.soft, buf);
+ err = fpregs_soft_set(current, NULL,
+ 0, sizeof(struct user_i387_ia32_struct),
+ NULL, buf) != 0;
}
set_used_math();
return err;
}
-/*
- * ptrace request handlers.
- */
+#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
-static inline int get_fpregs_fsave(struct user_i387_struct __user *buf,
- struct task_struct *tsk)
-{
- return __copy_to_user(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)
-{
- return convert_fxsr_to_user((struct _fpstate __user *)buf,
- &tsk->thread.i387.fxsave);
-}
+#ifdef CONFIG_X86_64
int get_fpregs(struct user_i387_struct __user *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);
- }
- } else {
- return save_i387_soft(&tsk->thread.i387.soft,
- (struct _fpstate __user *)buf);
- }
+ return xfpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
}
-static inline int set_fpregs_fsave(struct task_struct *tsk,
- struct user_i387_struct __user *buf)
+int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
{
- return __copy_from_user(&tsk->thread.i387.fsave, buf,
- sizeof(struct user_i387_struct));
+ return xfpregs_set(tsk, NULL, 0, sizeof(*buf), NULL, buf);
}
-static inline int set_fpregs_fxsave(struct task_struct *tsk,
- struct user_i387_struct __user *buf)
+#else
+
+int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk)
{
- return convert_fxsr_from_user(&tsk->thread.i387.fxsave,
- (struct _fpstate __user *)buf);
+ return fpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
}
int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
{
- if (HAVE_HWFP) {
- if (cpu_has_fxsr) {
- return set_fpregs_fxsave(tsk, buf);
- } else {
- return set_fpregs_fsave(tsk, buf);
- }
- } else {
- return restore_i387_soft(&tsk->thread.i387.soft,
- (struct _fpstate __user *)buf);
- }
+ return fpregs_set(tsk, NULL, 0, sizeof(*buf), NULL, buf);
}
int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *tsk)
{
- if (cpu_has_fxsr) {
- if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
- sizeof(struct user_fxsr_struct)))
- return -EFAULT;
- return 0;
- } else {
- return -EIO;
- }
+ return xfpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
}
int set_fpxregs(struct task_struct *tsk, struct user_fxsr_struct __user *buf)
{
- int ret = 0;
-
- if (cpu_has_fxsr) {
- if (__copy_from_user(&tsk->thread.i387.fxsave, buf,
- sizeof(struct user_fxsr_struct)))
- ret = -EFAULT;
- /* mxcsr reserved bits must be masked to zero
- * for security reasons */
- tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
- } else {
- ret = -EIO;
- }
- return ret;
+ return xfpregs_get(tsk, NULL, 0, sizeof(*buf), NULL, buf);
}
+#endif
+
/*
* FPU state for core dumps.
*/
@@ -538,7 +553,7 @@ int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
}
int dump_task_extended_fpu(struct task_struct *tsk,
- struct user_fxsr_struct *fpu)
+ struct user32_fxsr_struct *fpu)
{
int fpvalid = tsk_used_math(tsk) && cpu_has_fxsr;
diff --git a/include/asm-x86/i387.h b/include/asm-x86/i387.h
index a8bbed3..b2bc005 100644
--- a/include/asm-x86/i387.h
+++ b/include/asm-x86/i387.h
@@ -1,5 +1 @@
-#ifdef CONFIG_X86_32
-# include "i387_32.h"
-#else
-# include "i387_64.h"
-#endif
+#include "i387_64.h"
diff --git a/include/asm-x86/i387_64.h b/include/asm-x86/i387_64.h
index 4cea737..b52b891 100644
--- a/include/asm-x86/i387_64.h
+++ b/include/asm-x86/i387_64.h
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/kernel_stat.h>
+#include <linux/regset.h>
#include <asm/processor.h>
#include <asm/sigcontext.h>
#include <asm/user.h>
@@ -23,6 +24,10 @@ extern void mxcsr_feature_mask_init(void);
extern void init_fpu(struct task_struct *child);
extern asmlinkage void math_state_restore(void);
+extern user_regset_active_fn fpregs_active, xfpregs_active;
+extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get;
+extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
+
#ifdef CONFIG_X86_64
/* Ignore delayed exceptions from user space */
--
1.5.3.6
The i387_fxsave_struct formats really have the same layout
on 32 and 64, with only some slightly different use of a few
fields. The i387_fsave_struct and i387_soft_struct formats
are never used by 64-bit kernels, but it doesn't hurt to
have the unused types in the union and cuts down on the
amount of #ifdef hair required throughout the i387 code.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/asm-x86/processor.h | 92 +++++++++++++++++++-----------------------
1 files changed, 42 insertions(+), 50 deletions(-)
diff --git a/include/asm-x86/processor.h b/include/asm-x86/processor.h
index 80c5002..ea222cf 100644
--- a/include/asm-x86/processor.h
+++ b/include/asm-x86/processor.h
@@ -226,64 +226,37 @@ struct orig_ist {
unsigned long ist[7];
};
-#ifdef CONFIG_X86_32
-struct i387_fsave_struct {
- long cwd;
- long swd;
- long twd;
- long fip;
- long fcs;
- long foo;
- long fos;
- long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
- long status; /* software status information */
-};
-
-struct i387_fxsave_struct {
- unsigned short cwd;
- unsigned short swd;
- unsigned short twd;
- unsigned short fop;
- long fip;
- long fcs;
- long foo;
- long fos;
- long mxcsr;
- long mxcsr_mask;
- long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
- long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */
- long padding[56];
-} __attribute__((aligned(16)));
+#define MXCSR_DEFAULT 0x1f80
-struct i387_soft_struct {
- long cwd;
- long swd;
- long twd;
- long fip;
- long fcs;
- long foo;
- long fos;
- long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
- unsigned char ftop, changed, lookahead, no_update, rm, alimit;
- struct info *info;
- unsigned long entry_eip;
-};
-
-union i387_union {
- struct i387_fsave_struct fsave;
- struct i387_fxsave_struct fxsave;
- struct i387_soft_struct soft;
+struct i387_fsave_struct {
+ u32 cwd;
+ u32 swd;
+ u32 twd;
+ u32 fip;
+ u32 fcs;
+ u32 foo;
+ u32 fos;
+ u32 st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
+ u32 status; /* software status information */
};
-# include "processor_32.h"
-#else
struct i387_fxsave_struct {
u16 cwd;
u16 swd;
u16 twd;
u16 fop;
- u64 rip;
- u64 rdp;
+ union {
+ struct {
+ u64 rip;
+ u64 rdp;
+ };
+ struct {
+ u32 fip;
+ u32 fcs;
+ u32 foo;
+ u32 fos;
+ };
+ };
u32 mxcsr;
u32 mxcsr_mask;
u32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
@@ -291,10 +264,29 @@ struct i387_fxsave_struct {
u32 padding[24];
} __attribute__((aligned(16)));
+struct i387_soft_struct {
+ u32 cwd;
+ u32 swd;
+ u32 twd;
+ u32 fip;
+ u32 fcs;
+ u32 foo;
+ u32 fos;
+ u32 st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
+ u8 ftop, changed, lookahead, no_update, rm, alimit;
+ struct info *info;
+ u32 entry_eip;
+};
+
union i387_union {
+ struct i387_fsave_struct fsave;
struct i387_fxsave_struct fxsave;
+ struct i387_soft_struct soft;
};
+#ifdef CONFIG_X86_32
+# include "processor_32.h"
+#else
# include "processor_64.h"
#endif
--
1.5.3.6
This cleans up the TLS code to use struct desc_struct and to separate the
encoding and installation magic from the interface wrappers.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/tls.c | 89 ++++++++++++++++++++++++-----------------------
include/asm-x86/desc.h | 11 +++++-
2 files changed, 54 insertions(+), 46 deletions(-)
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c
index 98f428b..f11c92a 100644
--- a/arch/x86/kernel/tls.c
+++ b/arch/x86/kernel/tls.c
@@ -24,6 +24,29 @@ static int get_free_idx(void)
return -ESRCH;
}
+static void set_tls_desc(struct task_struct *p, int idx,
+ const struct user_desc *info)
+{
+ struct thread_struct *t = &p->thread;
+ struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
+ int cpu;
+
+ /*
+ * We must not get preempted while modifying the TLS.
+ */
+ cpu = get_cpu();
+
+ if (LDT_empty(info))
+ desc->a = desc->b = 0;
+ else
+ fill_ldt(desc, info);
+
+ if (t == ¤t->thread)
+ load_TLS(t, cpu);
+
+ put_cpu();
+}
+
/*
* Set a given TLS descriptor:
*/
@@ -31,10 +54,7 @@ int do_set_thread_area(struct task_struct *p, int idx,
struct user_desc __user *u_info,
int can_allocate)
{
- struct thread_struct *t = &p->thread;
struct user_desc info;
- u32 *desc;
- int cpu;
if (copy_from_user(&info, u_info, sizeof(info)))
return -EFAULT;
@@ -57,23 +77,8 @@ int do_set_thread_area(struct task_struct *p, int idx,
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
return -EINVAL;
- desc = (u32 *) &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
+ set_tls_desc(p, idx, &info);
- /*
- * We must not get preempted while modifying the TLS.
- */
- cpu = get_cpu();
-
- if (LDT_empty(&info)) {
- desc[0] = 0;
- desc[1] = 0;
- } else
- fill_ldt((struct desc_struct *)desc, &info);
-
- if (t == ¤t->thread)
- load_TLS(t, cpu);
-
- put_cpu();
return 0;
}
@@ -87,42 +92,38 @@ asmlinkage int sys_set_thread_area(struct user_desc __user *u_info)
* Get the current Thread-Local Storage area:
*/
-#define GET_LIMIT(desc) (((desc)[0] & 0x0ffff) | ((desc)[1] & 0xf0000))
-#define GET_32BIT(desc) (((desc)[1] >> 22) & 1)
-#define GET_CONTENTS(desc) (((desc)[1] >> 10) & 3)
-#define GET_WRITABLE(desc) (((desc)[1] >> 9) & 1)
-#define GET_LIMIT_PAGES(desc) (((desc)[1] >> 23) & 1)
-#define GET_PRESENT(desc) (((desc)[1] >> 15) & 1)
-#define GET_USEABLE(desc) (((desc)[1] >> 20) & 1)
-#define GET_LONGMODE(desc) (((desc)[1] >> 21) & 1)
+static void fill_user_desc(struct user_desc *info, int idx,
+ const struct desc_struct *desc)
+
+{
+ memset(info, 0, sizeof(*info));
+ info->entry_number = idx;
+ info->base_addr = get_desc_base(desc);
+ info->limit = get_desc_limit(desc);
+ info->seg_32bit = desc->d;
+ info->contents = desc->type >> 2;
+ info->read_exec_only = !(desc->type & 2);
+ info->limit_in_pages = desc->g;
+ info->seg_not_present = !desc->p;
+ info->useable = desc->avl;
+#ifdef CONFIG_X86_64
+ info->lm = desc->l;
+#endif
+}
int do_get_thread_area(struct task_struct *p, int idx,
struct user_desc __user *u_info)
{
- struct thread_struct *t = &p->thread;
struct user_desc info;
- u32 *desc;
if (idx == -1 && get_user(idx, &u_info->entry_number))
return -EFAULT;
+
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
return -EINVAL;
- desc = (u32 *) &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
-
- memset(&info, 0, sizeof(struct user_desc));
- info.entry_number = idx;
- info.base_addr = get_desc_base((struct desc_struct *)desc);
- info.limit = GET_LIMIT(desc);
- info.seg_32bit = GET_32BIT(desc);
- info.contents = GET_CONTENTS(desc);
- info.read_exec_only = !GET_WRITABLE(desc);
- info.limit_in_pages = GET_LIMIT_PAGES(desc);
- info.seg_not_present = !GET_PRESENT(desc);
- info.useable = GET_USEABLE(desc);
-#ifdef CONFIG_X86_64
- info.lm = GET_LONGMODE(desc);
-#endif
+ fill_user_desc(&info, idx,
+ &p->thread.tls_array[idx - GDT_ENTRY_TLS_MIN]);
if (copy_to_user(u_info, &info, sizeof(info)))
return -EFAULT;
diff --git a/include/asm-x86/desc.h b/include/asm-x86/desc.h
index 6c781bf..df28de6 100644
--- a/include/asm-x86/desc.h
+++ b/include/asm-x86/desc.h
@@ -7,7 +7,8 @@
#include <asm/mmu.h>
#include <linux/smp.h>
-static inline void fill_ldt(struct desc_struct *desc, struct user_desc *info)
+static inline void fill_ldt(struct desc_struct *desc,
+ const struct user_desc *info)
{
desc->limit0 = info->limit & 0x0ffff;
desc->base0 = info->base_addr & 0x0000ffff;
@@ -275,10 +276,16 @@ static inline void load_LDT(mm_context_t *pc)
preempt_enable();
}
-static inline unsigned long get_desc_base(struct desc_struct *desc)
+static inline unsigned long get_desc_base(const struct desc_struct *desc)
{
return desc->base0 | ((desc->base1) << 16) | ((desc->base2) << 24);
}
+
+static inline unsigned long get_desc_limit(const struct desc_struct *desc)
+{
+ return desc->limit0 | (desc->limit << 16);
+}
+
static inline void _set_gate(int gate, unsigned type, void *addr,
unsigned dpl, unsigned ist, unsigned seg)
{
--
1.5.3.6
This converts the ptrace/signal accessors for i387 math_emu
state to the user_regset interface style, and calls these
from the old interfaces.
It also cleans up math_emulate's ptrace check to be a
single-step check, which is what it really wants.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/math-emu/fpu_entry.c | 86 +++++++++++++++++++++++++---------------
1 files changed, 54 insertions(+), 32 deletions(-)
diff --git a/arch/x86/math-emu/fpu_entry.c b/arch/x86/math-emu/fpu_entry.c
index 377c60d..cfbdaa1 100644
--- a/arch/x86/math-emu/fpu_entry.c
+++ b/arch/x86/math-emu/fpu_entry.c
@@ -25,10 +25,11 @@
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
-#include <linux/ptrace.h>
+#include <linux/regset.h>
#include <asm/uaccess.h>
#include <asm/desc.h>
+#include <asm/user.h>
#include "fpu_system.h"
#include "fpu_emu.h"
@@ -198,9 +199,7 @@ asmlinkage void math_emulate(long arg)
code_limit = 0xffffffff;
}
- FPU_lookahead = 1;
- if (current->ptrace & PT_PTRACED)
- FPU_lookahead = 0;
+ FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
&addr_modes.override)) {
@@ -673,31 +672,37 @@ void math_abort(struct info *info, unsigned int signal)
#define sstatus_word() \
((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
-int restore_i387_soft(void *s387, struct _fpstate __user *buf)
+int fpregs_soft_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
- u_char __user *d = (u_char __user *) buf;
+ struct i387_soft_struct *s387 = &target->thread.i387.soft;
+ void *space = s387->st_space;
+ int ret;
int offset, other, i, tags, regnr, tag, newtop;
RE_ENTRANT_CHECK_OFF;
- FPU_access_ok(VERIFY_READ, d, 7 * 4 + 8 * 10);
- if (__copy_from_user(&S387->cwd, d, 7 * 4))
- return -1;
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
+ offsetof(struct i387_soft_struct, st_space));
RE_ENTRANT_CHECK_ON;
- d += 7 * 4;
+ if (ret)
+ return ret;
S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
offset = (S387->ftop & 7) * 10;
other = 80 - offset;
RE_ENTRANT_CHECK_OFF;
+
/* Copy all registers in stack order. */
- if (__copy_from_user(((u_char *) & S387->st_space) + offset, d, other))
- return -1;
- if (offset)
- if (__copy_from_user
- ((u_char *) & S387->st_space, d + other, offset))
- return -1;
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ space + offset, 0, other);
+ if (!ret && offset)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ space, 0, offset);
+
RE_ENTRANT_CHECK_ON;
/* The tags may need to be corrected now. */
@@ -716,16 +721,21 @@ int restore_i387_soft(void *s387, struct _fpstate __user *buf)
}
S387->twd = tags;
- return 0;
+ return ret;
}
-int save_i387_soft(void *s387, struct _fpstate __user * buf)
+int fpregs_soft_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
{
- u_char __user *d = (u_char __user *) buf;
+ struct i387_soft_struct *s387 = &target->thread.i387.soft;
+ const void *space = s387->st_space;
+ int ret;
int offset = (S387->ftop & 7) * 10, other = 80 - offset;
RE_ENTRANT_CHECK_OFF;
- FPU_access_ok(VERIFY_WRITE, d, 7 * 4 + 8 * 10);
+
#ifdef PECULIAR_486
S387->cwd &= ~0xe080;
/* An 80486 sets nearly all of the reserved bits to 1. */
@@ -735,21 +745,33 @@ int save_i387_soft(void *s387, struct _fpstate __user * buf)
S387->fcs &= ~0xf8000000;
S387->fos |= 0xffff0000;
#endif /* PECULIAR_486 */
- if (__copy_to_user(d, &S387->cwd, 7 * 4))
- return -1;
- RE_ENTRANT_CHECK_ON;
- d += 7 * 4;
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
+ offsetof(struct i387_soft_struct, st_space));
- RE_ENTRANT_CHECK_OFF;
/* Copy all registers in stack order. */
- if (__copy_to_user(d, ((u_char *) & S387->st_space) + offset, other))
- return -1;
- if (offset)
- if (__copy_to_user
- (d + other, (u_char *) & S387->st_space, offset))
- return -1;
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ space + offset, 0, other);
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ space, 0, offset);
+
RE_ENTRANT_CHECK_ON;
- return 1;
+ return ret;
+}
+
+int save_i387_soft(void *s387, struct _fpstate __user *buf)
+{
+ return fpregs_soft_get(current, NULL,
+ 0, sizeof(struct user_i387_struct),
+ NULL, buf) ? -1 : 1;
+}
+
+int restore_i387_soft(void *s387, struct _fpstate __user *buf)
+{
+ return fpregs_soft_set(current, NULL,
+ 0, sizeof(struct user_i387_struct),
+ NULL, buf) ? -1 : 1;
}
--
1.5.3.6
This adds accessor functions in the user_regset style for the TLS data.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/tls.c | 89 +++++++++++++++++++++++++++++++++++++++++++++---
arch/x86/kernel/tls.h | 21 +++++++++++
2 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c
index f11c92a..6dfd4e7 100644
--- a/arch/x86/kernel/tls.c
+++ b/arch/x86/kernel/tls.c
@@ -2,6 +2,7 @@
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/user.h>
+#include <linux/regset.h>
#include <asm/uaccess.h>
#include <asm/desc.h>
@@ -10,6 +11,8 @@
#include <asm/processor.h>
#include <asm/proto.h>
+#include "tls.h"
+
/*
* sys_alloc_thread_area: get a yet unused TLS descriptor index.
*/
@@ -25,7 +28,7 @@ static int get_free_idx(void)
}
static void set_tls_desc(struct task_struct *p, int idx,
- const struct user_desc *info)
+ const struct user_desc *info, int n)
{
struct thread_struct *t = &p->thread;
struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
@@ -36,10 +39,14 @@ static void set_tls_desc(struct task_struct *p, int idx,
*/
cpu = get_cpu();
- if (LDT_empty(info))
- desc->a = desc->b = 0;
- else
- fill_ldt(desc, info);
+ while (n-- > 0) {
+ if (LDT_empty(info))
+ desc->a = desc->b = 0;
+ else
+ fill_ldt(desc, info);
+ ++info;
+ ++desc;
+ }
if (t == ¤t->thread)
load_TLS(t, cpu);
@@ -77,7 +84,7 @@ int do_set_thread_area(struct task_struct *p, int idx,
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
return -EINVAL;
- set_tls_desc(p, idx, &info);
+ set_tls_desc(p, idx, &info, 1);
return 0;
}
@@ -134,3 +141,73 @@ asmlinkage int sys_get_thread_area(struct user_desc __user *u_info)
{
return do_get_thread_area(current, -1, u_info);
}
+
+int regset_tls_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ struct thread_struct *t = &target->thread;
+ int n = GDT_ENTRY_TLS_ENTRIES;
+ while (n > 0 && desc_empty(&t->tls_array[n - 1]))
+ --n;
+ return n;
+}
+
+int regset_tls_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const struct desc_struct *tls;
+
+ if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) ||
+ (pos % sizeof(struct user_desc)) != 0 ||
+ (count % sizeof(struct user_desc)) != 0)
+ return -EINVAL;
+
+ pos /= sizeof(struct user_desc);
+ count /= sizeof(struct user_desc);
+
+ tls = &target->thread.tls_array[pos];
+
+ if (kbuf) {
+ struct user_desc *info = kbuf;
+ while (count-- > 0)
+ fill_user_desc(info++, GDT_ENTRY_TLS_MIN + pos++,
+ tls++);
+ } else {
+ struct user_desc __user *u_info = ubuf;
+ while (count-- > 0) {
+ struct user_desc info;
+ fill_user_desc(&info, GDT_ENTRY_TLS_MIN + pos++, tls++);
+ if (__copy_to_user(u_info++, &info, sizeof(info)))
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+int regset_tls_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES];
+ const struct user_desc *info;
+
+ if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) ||
+ (pos % sizeof(struct user_desc)) != 0 ||
+ (count % sizeof(struct user_desc)) != 0)
+ return -EINVAL;
+
+ if (kbuf)
+ info = kbuf;
+ else if (__copy_from_user(infobuf, ubuf, count))
+ return -EFAULT;
+ else
+ info = infobuf;
+
+ set_tls_desc(target,
+ GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)),
+ info, count / sizeof(struct user_desc));
+
+ return 0;
+}
diff --git a/arch/x86/kernel/tls.h b/arch/x86/kernel/tls.h
new file mode 100644
index 0000000..2f083a2
--- /dev/null
+++ b/arch/x86/kernel/tls.h
@@ -0,0 +1,21 @@
+/*
+ * Internal declarations for x86 TLS implementation functions.
+ *
+ * Copyright (C) 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 _ARCH_X86_KERNEL_TLS_H
+
+#include <linux/regset.h>
+
+extern user_regset_active_fn regset_tls_active;
+extern user_regset_get_fn regset_tls_get;
+extern user_regset_set_fn regset_tls_set;
+
+#endif /* _ARCH_X86_KERNEL_TLS_H */
--
1.5.3.6
This defines task_user_regset_view and the tables
describing the x86 user_regset layouts for 32 and 64.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/ptrace.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 87 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index c520ad7..ede27e9 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -15,6 +15,7 @@
#include <linux/ptrace.h>
#include <linux/regset.h>
#include <linux/user.h>
+#include <linux/elf.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/seccomp.h>
@@ -32,6 +33,14 @@
#include <asm/proto.h>
#include <asm/ds.h>
+#include "tls.h"
+
+enum x86_regset {
+ REGSET_GENERAL,
+ REGSET_FP,
+ REGSET_XFP,
+ REGSET_TLS,
+};
/*
* does not yet catch signals sent when the child dies.
@@ -1336,6 +1345,84 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
#endif /* CONFIG_IA32_EMULATION */
+#ifdef CONFIG_X86_64
+
+static const struct user_regset x86_64_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(struct user_regs_struct) / sizeof(long),
+ .size = sizeof(long), .align = sizeof(long),
+ .get = genregs_get, .set = genregs_set
+ },
+ [REGSET_FP] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct user_i387_struct) / sizeof(long),
+ .size = sizeof(long), .align = sizeof(long),
+ .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
+ },
+};
+
+static const struct user_regset_view user_x86_64_view = {
+ .name = "x86_64", .e_machine = EM_X86_64,
+ .regsets = x86_64_regsets, .n = ARRAY_SIZE(x86_64_regsets)
+};
+
+#else /* CONFIG_X86_32 */
+
+#define user_regs_struct32 user_regs_struct
+#define genregs32_get genregs_get
+#define genregs32_set genregs_set
+
+#endif /* CONFIG_X86_64 */
+
+#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
+static const struct user_regset x86_32_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(struct user_regs_struct32) / sizeof(u32),
+ .size = sizeof(u32), .align = sizeof(u32),
+ .get = genregs32_get, .set = genregs32_set
+ },
+ [REGSET_FP] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct user_i387_struct) / sizeof(u32),
+ .size = sizeof(u32), .align = sizeof(u32),
+ .active = fpregs_active, .get = fpregs_get, .set = fpregs_set
+ },
+ [REGSET_XFP] = {
+ .core_note_type = NT_PRXFPREG,
+ .n = sizeof(struct user_i387_struct) / sizeof(u32),
+ .size = sizeof(u32), .align = sizeof(u32),
+ .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
+ },
+ [REGSET_TLS] = {
+ .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN,
+ .size = sizeof(struct user_desc),
+ .align = sizeof(struct user_desc),
+ .active = regset_tls_active,
+ .get = regset_tls_get, .set = regset_tls_set
+ },
+};
+
+static const struct user_regset_view user_x86_32_view = {
+ .name = "i386", .e_machine = EM_386,
+ .regsets = x86_32_regsets, .n = ARRAY_SIZE(x86_32_regsets)
+};
+#endif
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+#ifdef CONFIG_IA32_EMULATION
+ if (test_tsk_thread_flag(task, TIF_IA32))
+#endif
+#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
+ return &user_x86_32_view;
+#endif
+#ifdef CONFIG_X86_64
+ return &user_x86_64_view;
+#endif
+}
+
#ifdef CONFIG_X86_32
void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
--
1.5.3.6
This provides the task_user_regset_view entry point and support for all the
native-mode (64 on CONFIG_PPC64, 32 on CONFIG_PPC32) thread register state.
This will enable generic machine-independent code to access user-mode
threads' registers for debugging and dumping.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/Makefile | 2 +
arch/powerpc/kernel/ptrace.c | 52 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index ca51f0c..615ed96 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -2,6 +2,8 @@
# Makefile for the linux kernel.
#
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
ifeq ($(CONFIG_PPC64),y)
EXTRA_CFLAGS += -mno-minimal-toc
endif
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index e493fc0..b32ac5f 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -455,6 +455,58 @@ static int set_evrregs(struct task_struct *task, unsigned long *data)
#endif /* CONFIG_SPE */
+/*
+ * These are our native regset flavors.
+ */
+enum powerpc_regset {
+ REGSET_GPR,
+ REGSET_FPR,
+#ifdef CONFIG_ALTIVEC
+ REGSET_VMX,
+#endif
+#ifdef CONFIG_SPE
+ REGSET_SPE,
+#endif
+};
+
+static const struct user_regset native_regsets[] = {
+ [REGSET_GPR] = {
+ .core_note_type = NT_PRSTATUS, .n = ELF_NGREG,
+ .size = sizeof(long), .align = sizeof(long),
+ .get = gpr_get, .set = gpr_set
+ },
+ [REGSET_FPR] = {
+ .core_note_type = NT_PRFPREG, .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .get = fpr_get, .set = fpr_set
+ },
+#ifdef CONFIG_ALTIVEC
+ [REGSET_VMX] = {
+ .core_note_type = NT_PPC_VMX, .n = 34,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .active = vr_active, .get = vr_get, .set = vr_set
+ },
+#endif
+#ifdef CONFIG_SPE
+ [REGSET_SPE] = {
+ .n = 35,
+ .size = sizeof(u32), .align = sizeof(u32),
+ .active = evr_active, .get = evr_get, .set = evr_set
+ },
+#endif
+};
+
+static const struct user_regset_view user_ppc_native_view = {
+ .name = UTS_MACHINE, .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI,
+ .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+ return &user_ppc_native_view;
+}
+
+
void user_enable_single_step(struct task_struct *task)
{
struct pt_regs *regs = task->thread.regs;
--
1.5.3.6
This switches the CONFIG_PPC64 support for 32-bit ELF to use the generic
fs/compat_binfmt_elf.c implementation instead of our own binfmt_elf32.c.
Since so much is the same between 32/64, there is only one macro we have to
define to make the generic support work out of the box.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/Makefile | 3 +-
arch/powerpc/kernel/binfmt_elf32.c | 69 ------------------------------------
include/asm-powerpc/elf.h | 1 +
3 files changed, 3 insertions(+), 70 deletions(-)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 615ed96..2cbe58a 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -17,11 +17,12 @@ obj-y := semaphore.o cputable.o ptrace.o syscalls.o \
init_task.o process.o systbl.o idle.o \
signal.o
obj-y += vdso32/
-obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \
+obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \
signal_64.o ptrace32.o \
paca.o cpu_setup_ppc970.o \
cpu_setup_pa6t.o \
firmware.o sysfs.o nvram_64.o
+obj-$(CONFIG_PPC64) += ../../../fs/compat_binfmt_elf.o
obj-$(CONFIG_PPC64) += vdso64/
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
obj-$(CONFIG_PPC_970_NAP) += idle_power4.o
diff --git a/arch/powerpc/kernel/binfmt_elf32.c b/arch/powerpc/kernel/binfmt_elf32.c
deleted file mode 100644
index 1d45d77..0000000
--- a/arch/powerpc/kernel/binfmt_elf32.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons.
- * based on the SPARC64 version.
- * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller ([email protected])
- * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek ([email protected])
- *
- * Copyright (C) 2000,2001 Ken Aaker ([email protected]), IBM Corp
- * Copyright (C) 2001 Anton Blanchard ([email protected]), IBM
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <asm/processor.h>
-#include <linux/module.h>
-#include <linux/compat.h>
-#include <linux/elfcore-compat.h>
-
-#undef ELF_ARCH
-#undef ELF_CLASS
-#define ELF_CLASS ELFCLASS32
-#define ELF_ARCH EM_PPC
-
-#undef elfhdr
-#undef elf_phdr
-#undef elf_note
-#undef elf_addr_t
-#define elfhdr elf32_hdr
-#define elf_phdr elf32_phdr
-#define elf_note elf32_note
-#define elf_addr_t Elf32_Off
-
-#define elf_prstatus compat_elf_prstatus
-#define elf_prpsinfo compat_elf_prpsinfo
-
-#define elf_core_copy_regs compat_elf_core_copy_regs
-static inline void compat_elf_core_copy_regs(compat_elf_gregset_t *elf_regs,
- struct pt_regs *regs)
-{
- PPC_ELF_CORE_COPY_REGS((*elf_regs), regs);
-}
-
-#define elf_core_copy_task_regs compat_elf_core_copy_task_regs
-static int compat_elf_core_copy_task_regs(struct task_struct *tsk,
- compat_elf_gregset_t *elf_regs)
-{
- struct pt_regs *regs = tsk->thread.regs;
- if (regs)
- compat_elf_core_copy_regs(elf_regs, regs);
- return 1;
-}
-
-#include <linux/time.h>
-
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-static __inline__ void
-cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
-{
- unsigned long jiffies = cputime_to_jiffies(cputime);
- value->tv_usec = (jiffies % HZ) * (1000000L / HZ);
- value->tv_sec = jiffies / HZ;
-}
-
-#define init_elf_binfmt init_elf32_binfmt
-
-#include "../../../fs/binfmt_elf.c"
diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h
index fd9bf8b..9080d85 100644
--- a/include/asm-powerpc/elf.h
+++ b/include/asm-powerpc/elf.h
@@ -165,6 +165,7 @@ typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32];
* This is used to ensure we don't load something for the wrong architecture.
*/
#define elf_check_arch(x) ((x)->e_machine == ELF_ARCH)
+#define compat_elf_check_arch(x) ((x)->e_machine == EM_PPC)
#define USE_ELF_CORE_DUMP
#define CORE_DUMP_USE_REGSET
--
1.5.3.6
This adds accessor functions in the user_regset style for
the general registers (struct user_regs_struct).
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/ptrace.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 18972a3..c520ad7 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -13,6 +13,7 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/regset.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
@@ -368,6 +369,59 @@ static unsigned long getreg(struct task_struct *task, unsigned long offset)
return *pt_regs_access(task_pt_regs(task), offset);
}
+static int genregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (kbuf) {
+ unsigned long *k = kbuf;
+ while (count > 0) {
+ *k++ = getreg(target, pos);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ unsigned long __user *u = ubuf;
+ while (count > 0) {
+ if (__put_user(getreg(target, pos), u++))
+ return -EFAULT;
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+
+ return 0;
+}
+
+static int genregs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+ if (kbuf) {
+ const unsigned long *k = kbuf;
+ while (count > 0 && !ret) {
+ ret = putreg(target, pos, *k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ const unsigned long __user *u = ubuf;
+ while (count > 0 && !ret) {
+ unsigned long word;
+ ret = __get_user(word, u++);
+ if (ret)
+ break;
+ ret = putreg(target, pos, word);
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+ return ret;
+}
+
/*
* This function is trivial and will be inlined by the compiler.
* Having it separates the implementation details of debug
@@ -1009,6 +1063,61 @@ static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
#undef R32
#undef SEG32
+static int genregs32_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (kbuf) {
+ compat_ulong_t *k = kbuf;
+ while (count > 0) {
+ getreg32(target, pos, k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ compat_ulong_t __user *u = ubuf;
+ while (count > 0) {
+ compat_ulong_t word;
+ getreg32(target, pos, &word);
+ if (__put_user(word, u++))
+ return -EFAULT;
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+
+ return 0;
+}
+
+static int genregs32_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret = 0;
+ if (kbuf) {
+ const compat_ulong_t *k = kbuf;
+ while (count > 0 && !ret) {
+ ret = putreg(target, pos, *k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ const compat_ulong_t __user *u = ubuf;
+ while (count > 0 && !ret) {
+ compat_ulong_t word;
+ ret = __get_user(word, u++);
+ if (ret)
+ break;
+ ret = putreg(target, pos, word);
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+ return ret;
+}
+
static long ptrace32_siginfo(unsigned request, u32 pid, u32 addr, u32 data)
{
siginfo_t __user *si = compat_alloc_user_space(sizeof(siginfo_t));
--
1.5.3.6
Now that ptrace_request handles these, we can drop some more boilerplate.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 12 ------------
1 files changed, 0 insertions(+), 12 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index eb27bd9..3e228d6 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -697,12 +697,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
int ret = -EPERM;
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:
- ret = generic_ptrace_peekdata(child, addr, data);
- break;
-
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long index, tmp;
@@ -730,12 +724,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break;
}
- /* If 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 = generic_ptrace_pokedata(child, addr, data);
- break;
-
/* write the word at location addr in the USER area */
case PTRACE_POKEUSR: {
unsigned long index;
--
1.5.3.6
This extends task_user_regset_view CONFIG_PPC64 with support for the 32-bit
view of register state, compatible with what a CONFIG_PPC32 kernel provides.
This will enable generic machine-independent code to access user-mode
threads' registers for debugging and dumping.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 158 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 158 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index b32ac5f..e961e10 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -501,8 +501,166 @@ static const struct user_regset_view user_ppc_native_view = {
.regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
};
+#ifdef CONFIG_PPC64
+#include <linux/compat.h>
+
+static int gpr32_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const unsigned long *regs = &target->thread.regs->gpr[0];
+ compat_ulong_t *k = kbuf;
+ compat_ulong_t __user *u = ubuf;
+ compat_ulong_t reg;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+
+ pos /= sizeof(reg);
+ count /= sizeof(reg);
+
+ if (kbuf)
+ for (; count > 0 && pos < PT_MSR; --count)
+ *k++ = regs[pos++];
+ else
+ for (; count > 0 && pos < PT_MSR; --count)
+ if (__put_user((compat_ulong_t) regs[pos++], u++))
+ return -EFAULT;
+
+ if (count > 0 && pos == PT_MSR) {
+ reg = get_user_msr(target);
+ if (kbuf)
+ *k++ = reg;
+ else if (__put_user(reg, u++))
+ return -EFAULT;
+ ++pos;
+ --count;
+ }
+
+ if (kbuf)
+ for (; count > 0 && pos < PT_REGS_COUNT; --count)
+ *k++ = regs[pos++];
+ else
+ for (; count > 0 && pos < PT_REGS_COUNT; --count)
+ if (__put_user((compat_ulong_t) regs[pos++], u++))
+ return -EFAULT;
+
+ pos *= sizeof(reg);
+ count *= sizeof(reg);
+ return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ PT_REGS_COUNT * sizeof(reg), -1);
+}
+
+static int gpr32_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned long *regs = &target->thread.regs->gpr[0];
+ const compat_ulong_t *k = kbuf;
+ const compat_ulong_t __user *u = ubuf;
+ compat_ulong_t reg;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+
+ pos /= sizeof(reg);
+ count /= sizeof(reg);
+
+ if (kbuf)
+ for (; count > 0 && pos < PT_MSR; --count)
+ regs[pos++] = *k++;
+ else
+ for (; count > 0 && pos < PT_MSR; --count) {
+ if (__get_user(reg, u++))
+ return -EFAULT;
+ regs[pos++] = reg;
+ }
+
+
+ if (count > 0 && pos == PT_MSR) {
+ if (kbuf)
+ reg = *k++;
+ else if (__get_user(reg, u++))
+ return -EFAULT;
+ set_user_msr(target, reg);
+ ++pos;
+ --count;
+ }
+
+ if (kbuf)
+ for (; count > 0 && pos <= PT_MAX_PUT_REG; --count)
+ regs[pos++] = *k++;
+ else
+ for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) {
+ if (__get_user(reg, u++))
+ return -EFAULT;
+ regs[pos++] = reg;
+ }
+
+ if (count > 0 && pos == PT_TRAP) {
+ if (kbuf)
+ reg = *k++;
+ else if (__get_user(reg, u++))
+ return -EFAULT;
+ set_user_trap(target, reg);
+ ++pos;
+ --count;
+ }
+
+ pos *= sizeof(reg);
+ count *= sizeof(reg);
+ return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ (PT_TRAP + 1) * sizeof(reg), -1);
+}
+
+/*
+ * These are the regset flavors matching the CONFIG_PPC32 native set.
+ */
+static const struct user_regset compat_regsets[] = {
+ [REGSET_GPR] = {
+ .core_note_type = NT_PRSTATUS, .n = ELF_NGREG,
+ .size = sizeof(compat_long_t), .align = sizeof(compat_long_t),
+ .get = gpr32_get, .set = gpr32_set
+ },
+ [REGSET_FPR] = {
+ .core_note_type = NT_PRFPREG, .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .get = fpr_get, .set = fpr_set
+ },
+#ifdef CONFIG_ALTIVEC
+ [REGSET_VMX] = {
+ .core_note_type = NT_PPC_VMX, .n = 34,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .active = vr_active, .get = vr_get, .set = vr_set
+ },
+#endif
+#ifdef CONFIG_SPE
+ [REGSET_SPE] = {
+ .n = 35,
+ .size = sizeof(u32), .align = sizeof(u32),
+ .active = evr_active, .get = evr_get, .set = evr_set
+ },
+#endif
+};
+
+static const struct user_regset_view user_ppc_compat_view = {
+ .name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI,
+ .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets)
+};
+#endif /* CONFIG_PPC64 */
+
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
+#ifdef CONFIG_PPC64
+ if (test_tsk_thread_flag(task, TIF_32BIT))
+ return &user_ppc_compat_view;
+#endif
return &user_ppc_native_view;
}
--
1.5.3.6
This moves some code into asm-x86/i387_64.h in preparation for
unifying this code between 32 and 64. The 32-bit versions of
some things are copied in some existing names changed to match
32-bit names and share code. For 64, save_i387 is moved into
an inline from i387_64.c; this matches restore_i387, which is
already an inline, and makes sense since there is exactly one
caller (in signal_64.c). The save_i387 function could use more
cosmetic cleanup, but it is just moved verbatim in this patch.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/i387_64.c | 31 -----
include/asm-x86/i387_64.h | 315 +++++++++++++++++++++++++++++++++------------
2 files changed, 233 insertions(+), 113 deletions(-)
diff --git a/arch/x86/kernel/i387_64.c b/arch/x86/kernel/i387_64.c
index bfaff28..f335a76 100644
--- a/arch/x86/kernel/i387_64.c
+++ b/arch/x86/kernel/i387_64.c
@@ -72,37 +72,6 @@ void init_fpu(struct task_struct *child)
}
/*
- * Signal frame handlers.
- */
-
-int save_i387(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
- int err = 0;
-
- BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
- sizeof(tsk->thread.i387.fxsave));
-
- if ((unsigned long)buf % 16)
- printk("save_i387: bad fpstate %p\n",buf);
-
- if (!used_math())
- return 0;
- clear_used_math(); /* trigger finit */
- if (task_thread_info(tsk)->status & TS_USEDFPU) {
- err = save_i387_checking((struct i387_fxsave_struct __user *)buf);
- if (err) return err;
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
- stts();
- } else {
- if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
- sizeof(struct i387_fxsave_struct)))
- return -1;
- }
- return 1;
-}
-
-/*
* ptrace request handlers.
*/
diff --git a/include/asm-x86/i387_64.h b/include/asm-x86/i387_64.h
index 12edb9f..4cea737 100644
--- a/include/asm-x86/i387_64.h
+++ b/include/asm-x86/i387_64.h
@@ -7,33 +7,23 @@
* x86-64 work by Andi Kleen 2002
*/
-#ifndef __ASM_X86_64_I387_H
-#define __ASM_X86_64_I387_H
+#ifndef _ASM_X86_I387_H
+#define _ASM_X86_I387_H
#include <linux/sched.h>
+#include <linux/kernel_stat.h>
#include <asm/processor.h>
#include <asm/sigcontext.h>
#include <asm/user.h>
-#include <asm/thread_info.h>
#include <asm/uaccess.h>
extern void fpu_init(void);
extern unsigned int mxcsr_feature_mask;
extern void mxcsr_feature_mask_init(void);
extern void init_fpu(struct task_struct *child);
-extern int save_i387(struct _fpstate __user *buf);
extern asmlinkage void math_state_restore(void);
-/*
- * FPU lazy state save handling...
- */
-
-#define unlazy_fpu(tsk) do { \
- if (task_thread_info(tsk)->status & TS_USEDFPU) \
- save_init_fpu(tsk); \
- else \
- tsk->fpu_counter = 0; \
-} while (0)
+#ifdef CONFIG_X86_64
/* Ignore delayed exceptions from user space */
static inline void tolerant_fwait(void)
@@ -46,52 +36,8 @@ static inline void tolerant_fwait(void)
" .previous\n");
}
-#define clear_fpu(tsk) do { \
- if (task_thread_info(tsk)->status & TS_USEDFPU) { \
- tolerant_fwait(); \
- task_thread_info(tsk)->status &= ~TS_USEDFPU; \
- stts(); \
- } \
-} while (0)
-
-/*
- * ptrace request handers...
- */
-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);
-
-/*
- * i387 state interaction
- */
-#define get_fpu_mxcsr(t) ((t)->thread.i387.fxsave.mxcsr)
-#define get_fpu_cwd(t) ((t)->thread.i387.fxsave.cwd)
-#define get_fpu_fxsr_twd(t) ((t)->thread.i387.fxsave.twd)
-#define get_fpu_swd(t) ((t)->thread.i387.fxsave.swd)
-#define set_fpu_cwd(t,val) ((t)->thread.i387.fxsave.cwd = (val))
-#define set_fpu_swd(t,val) ((t)->thread.i387.fxsave.swd = (val))
-#define set_fpu_fxsr_twd(t,val) ((t)->thread.i387.fxsave.twd = (val))
-
-#define X87_FSW_ES (1 << 7) /* Exception Summary */
-
-/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
- is pending. Clear the x87 state here by setting it to fixed
- values. The kernel data segment can be sometimes 0 and sometimes
- new user value. Both should be ok.
- Use the PDA as safe address because it should be already in L1. */
-static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
+static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
{
- if (unlikely(fx->swd & X87_FSW_ES))
- asm volatile("fnclex");
- alternative_input(ASM_NOP8 ASM_NOP2,
- " emms\n" /* clear stack tags */
- " fildl %%gs:0", /* load to clear state */
- X86_FEATURE_FXSAVE_LEAK);
-}
-
-static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
-{
int err;
asm volatile("1: rex64/fxrstor (%[fx])\n\t"
@@ -105,7 +51,7 @@ static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
" .quad 1b,3b\n"
".previous"
: [err] "=r" (err)
-#if 0 /* See comment in __fxsave_clear() below. */
+#if 0 /* See comment in __save_init_fpu() below. */
: [fx] "r" (fx), "m" (*fx), "0" (0));
#else
: [fx] "cdaSDb" (fx), "m" (*fx), "0" (0));
@@ -113,10 +59,27 @@ static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
if (unlikely(err))
init_fpu(current);
return err;
-}
+}
-static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
-{
+#define X87_FSW_ES (1 << 7) /* Exception Summary */
+
+/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
+ is pending. Clear the x87 state here by setting it to fixed
+ values. The kernel data segment can be sometimes 0 and sometimes
+ new user value. Both should be ok.
+ Use the PDA as safe address because it should be already in L1. */
+static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
+{
+ if (unlikely(fx->swd & X87_FSW_ES))
+ asm volatile("fnclex");
+ alternative_input(ASM_NOP8 ASM_NOP2,
+ " emms\n" /* clear stack tags */
+ " fildl %%gs:0", /* load to clear state */
+ X86_FEATURE_FXSAVE_LEAK);
+}
+
+static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
+{
int err;
asm volatile("1: rex64/fxsave (%[fx])\n\t"
@@ -139,9 +102,9 @@ static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
err = -EFAULT;
/* No need to clear here because the caller clears USED_MATH */
return err;
-}
+}
-static inline void __fxsave_clear(struct task_struct *tsk)
+static inline void __save_init_fpu(struct task_struct *tsk)
{
/* Using "rex64; fxsave %0" is broken because, if the memory operand
uses any extended registers for addressing, a second REX prefix
@@ -169,18 +132,142 @@ static inline void __fxsave_clear(struct task_struct *tsk)
thread.i387.fxsave)));
#endif
clear_fpu_state(&tsk->thread.i387.fxsave);
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+}
+
+/*
+ * Signal frame handlers.
+ */
+
+static inline int save_i387(struct _fpstate __user *buf)
+{
+ struct task_struct *tsk = current;
+ int err = 0;
+
+ BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
+ sizeof(tsk->thread.i387.fxsave));
+
+ if ((unsigned long)buf % 16)
+ printk("save_i387: bad fpstate %p\n", buf);
+
+ if (!used_math())
+ return 0;
+ clear_used_math(); /* trigger finit */
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ err = save_i387_checking((struct i387_fxsave_struct __user *)buf);
+ if (err) return err;
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+ stts();
+ } else {
+ if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
+ sizeof(struct i387_fxsave_struct)))
+ return -1;
+ }
+ return 1;
+}
+
+/*
+ * This restores directly out of user space. Exceptions are handled.
+ */
+static inline int restore_i387(struct _fpstate __user *buf)
+{
+ set_used_math();
+ if (!(task_thread_info(current)->status & TS_USEDFPU)) {
+ clts();
+ task_thread_info(current)->status |= TS_USEDFPU;
+ }
+ return restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
+}
+
+#else /* CONFIG_X86_32 */
+
+static inline void tolerant_fwait(void)
+{
+ asm volatile("fnclex ; fwait");
+}
+
+static inline void restore_fpu(struct task_struct *tsk)
+{
+ /*
+ * The "nop" is needed to make the instructions the same
+ * length.
+ */
+ alternative_input(
+ "nop ; frstor %1",
+ "fxrstor %1",
+ X86_FEATURE_FXSR,
+ "m" ((tsk)->thread.i387.fxsave));
+}
+
+/* We need a safe address that is cheap to find and that is already
+ in L1 during context switch. The best choices are unfortunately
+ different for UP and SMP */
+#ifdef CONFIG_SMP
+#define safe_address (__per_cpu_offset[0])
+#else
+#define safe_address (kstat_cpu(0).cpustat.user)
+#endif
+
+/*
+ * These must be called with preempt disabled
+ */
+static inline void __save_init_fpu(struct task_struct *tsk)
+{
+ /* Use more nops than strictly needed in case the compiler
+ varies code */
+ alternative_input(
+ "fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4,
+ "fxsave %[fx]\n"
+ "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:",
+ X86_FEATURE_FXSR,
+ [fx] "m" (tsk->thread.i387.fxsave),
+ [fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory");
+ /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
+ is pending. Clear the x87 state here by setting it to fixed
+ values. safe_address is a random variable that should be in L1 */
+ alternative_input(
+ GENERIC_NOP8 GENERIC_NOP2,
+ "emms\n\t" /* clear stack tags */
+ "fildl %[addr]", /* set F?P to defined value */
+ X86_FEATURE_FXSAVE_LEAK,
+ [addr] "m" (safe_address));
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+}
+
+/*
+ * Signal frame handlers...
+ */
+extern int save_i387(struct _fpstate __user *buf);
+extern int restore_i387(struct _fpstate __user *buf);
+
+#endif /* CONFIG_X86_64 */
+
+static inline void __unlazy_fpu(struct task_struct *tsk)
+{
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ __save_init_fpu(tsk);
+ stts();
+ } else
+ tsk->fpu_counter = 0;
+}
+
+static inline void __clear_fpu(struct task_struct *tsk)
+{
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ tolerant_fwait();
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+ stts();
+ }
}
static inline void kernel_fpu_begin(void)
{
struct thread_info *me = current_thread_info();
preempt_disable();
- if (me->status & TS_USEDFPU) {
- __fxsave_clear(me->task);
- me->status &= ~TS_USEDFPU;
- return;
- }
- clts();
+ if (me->status & TS_USEDFPU)
+ __save_init_fpu(me->task);
+ else
+ clts();
}
static inline void kernel_fpu_end(void)
@@ -189,24 +276,88 @@ static inline void kernel_fpu_end(void)
preempt_enable();
}
+#ifdef CONFIG_X86_64
+
static inline void save_init_fpu(struct task_struct *tsk)
{
- __fxsave_clear(tsk);
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
+ __save_init_fpu(tsk);
stts();
}
-/*
- * This restores directly out of user space. Exceptions are handled.
+#define unlazy_fpu __unlazy_fpu
+#define clear_fpu __clear_fpu
+
+#else /* CONFIG_X86_32 */
+
+/*
+ * These disable preemption on their own and are safe
*/
-static inline int restore_i387(struct _fpstate __user *buf)
+static inline void save_init_fpu(struct task_struct *tsk)
{
- set_used_math();
- if (!(task_thread_info(current)->status & TS_USEDFPU)) {
- clts();
- task_thread_info(current)->status |= TS_USEDFPU;
+ preempt_disable();
+ __save_init_fpu(tsk);
+ stts();
+ preempt_enable();
+}
+
+static inline void unlazy_fpu(struct task_struct *tsk)
+{
+ preempt_disable();
+ __unlazy_fpu(tsk);
+ preempt_enable();
+}
+
+static inline void clear_fpu(struct task_struct *tsk)
+{
+ preempt_disable();
+ __clear_fpu(tsk);
+ preempt_enable();
+}
+
+#endif /* CONFIG_X86_64 */
+
+/*
+ * 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);
+
+struct user_fxsr_struct;
+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);
+
+/*
+ * i387 state interaction
+ */
+static inline unsigned short get_fpu_cwd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.cwd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.cwd;
+ }
+}
+
+static inline unsigned short get_fpu_swd(struct task_struct *tsk)
+{
+ if (cpu_has_fxsr) {
+ return tsk->thread.i387.fxsave.swd;
+ } else {
+ return (unsigned short)tsk->thread.i387.fsave.swd;
+ }
+}
+
+static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk)
+{
+ if (cpu_has_xmm) {
+ return tsk->thread.i387.fxsave.mxcsr;
+ } else {
+ return MXCSR_DEFAULT;
}
- return restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
}
-#endif /* __ASM_X86_64_I387_H */
+#endif /* _ASM_X86_I387_H */
--
1.5.3.6
This adds hard-wired definitions for the remaining cpu_has_* macros
that correspond to flags required-features.h demands are set for
64-bit. Using these can efficiently avoid some #ifdef's when
merging 32-bit and 64-bit code together.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
include/asm-x86/cpufeature.h | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/include/asm-x86/cpufeature.h b/include/asm-x86/cpufeature.h
index 87dd900..75e2f78 100644
--- a/include/asm-x86/cpufeature.h
+++ b/include/asm-x86/cpufeature.h
@@ -192,9 +192,21 @@
#undef cpu_has_centaur_mcr
#define cpu_has_centaur_mcr 0
+#undef cpu_has_pse
+#define cpu_has_pse 1
+
#undef cpu_has_pge
#define cpu_has_pge 1
+#undef cpu_has_xmm
+#define cpu_has_xmm 1
+
+#undef cpu_has_xmm2
+#define cpu_has_xmm2 1
+
+#undef cpu_has_fxsr
+#define cpu_has_fxsr 1
+
#endif /* CONFIG_X86_64 */
#endif /* _ASM_X86_CPUFEATURE_H */
--
1.5.3.6
This makes ELF core dumps of 32-bit processes include a new
note type NT_386_TLS (0x200) giving the contents of the TLS
slots in struct user_desc format. This lets post mortem
examination figure out what the segment registers mean like
the debugger does with get_thread_area on a live process.
Signed-off-by: Roland McGrath <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
---
arch/x86/kernel/ptrace.c | 1 +
include/linux/elf.h | 1 +
2 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index e8c2ba3..25b6578 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -1314,6 +1314,7 @@ static const struct user_regset x86_32_regsets[] = {
.active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
},
[REGSET_TLS] = {
+ .core_note_type = NT_386_TLS,
.n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN,
.size = sizeof(struct user_desc),
.align = sizeof(struct user_desc),
diff --git a/include/linux/elf.h b/include/linux/elf.h
index ba268b2..30eb6fb 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -356,6 +356,7 @@ typedef struct elf64_shdr {
#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */
#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
+#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
/* Note header in a PT_NOTE section */
--
1.5.3.6
This removes duplicated code by calling the generic ptrace_request and
compat_ptrace_request functions for the things they already handle.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 37 +------------------------------------
1 files changed, 1 insertions(+), 36 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 25b6578..dac5503 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -760,12 +760,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
unsigned long __user *datap = (unsigned long __user *)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:
- ret = generic_ptrace_peekdata(child, addr, data);
- break;
-
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
@@ -787,12 +781,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long 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 = generic_ptrace_pokedata(child, addr, data);
- break;
-
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
ret = -EIO;
if ((addr & (sizeof(data) - 1)) || addr < 0 ||
@@ -1184,24 +1172,6 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
childregs = task_pt_regs(child);
switch (request) {
- case PTRACE_PEEKDATA:
- case PTRACE_PEEKTEXT:
- ret = 0;
- if (access_process_vm(child, addr, &val, sizeof(u32), 0) !=
- sizeof(u32))
- ret = -EIO;
- else
- ret = put_user(val, (unsigned int __user *)datap);
- break;
-
- case PTRACE_POKEDATA:
- case PTRACE_POKETEXT:
- ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(u32), 1) !=
- sizeof(u32))
- ret = -EIO;
- break;
-
case PTRACE_PEEKUSR:
ret = getreg32(child, addr, &val);
if (ret == 0)
@@ -1247,13 +1217,8 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
sizeof(struct user32_fxsr_struct),
datap);
- case PTRACE_GETEVENTMSG:
- ret = put_user(child->ptrace_message,
- (unsigned int __user *)compat_ptr(data));
- break;
-
default:
- BUG();
+ return compat_ptrace_request(child, request, addr, data);
}
out:
--
1.5.3.6
* Roland McGrath <[email protected]> wrote:
> This is a large series of patches, but there are only a couple that
> you need to read in detail to know how to get started on cleaning up
> your arch code (1, 4, 6).
>
> user_regset is a new kernel-internal interface into the arch code for
> accessing the user-space view of machine-specific state (registers et
> al--everything machine-specific that is visible via ptrace and the
> like, or should be). The idea is that arch code will have just one
> place it has to support fetching and changing the user-visible machine
> state of a user thread. This same interface can be used for writing
> core dumps, to underlie the implementation of PTRACE_GETREGS,
> PTRACE_SETREGS, and the like, and by any new set of debugging
> facilities that might come along.
[...]
> Patches 26 through 43 affect only arch/x86 code. I have not CC'd
> these ones to linux-arch. They include a bunch of cleanup that is
> specific to the idiosyncracies of the x86 code and isn't interesting
> as an example for what another arch would do.
thanks Roland - this is a really impressive set of cleanups
generalizations!
Testing feedback: i've put the x86 and core bits into x86.git and your
regset series has so far successfully passed a couple of hundred
iterations of random-qa on 32-bit and 64-bit x86 as well. (with a few
ptrace tests added to the mix as well) So it's all green as far as
arch/x86 and core goes :-)
Ingo
On Thu, Dec 20, 2007 at 03:53:57AM -0800, Roland McGrath wrote:
> +/*
> + * User-mode machine state access
> + *
> + * Copyright (C) 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.
What's a Red Hat Author? Sorry for the nitpicking, but why don't you
just use Author like everyone else?
On Thu, Dec 20, 2007 at 03:55:51AM -0800, Roland McGrath wrote:
> This adds a generic definition of compat_sys_ptrace that calls
> compat_arch_ptrace, parallel to sys_ptrace/arch_ptrace. Some
> machines needing this already define a function by that name.
> The new generic function is defined only on machines that
> put #define __ARCH_WANT_COMPAT_SYS_PTRACE into asm/ptrace.h.
Nice, we should have unified the compat ptrace code long ago.
Any chance you could make the ifdef symetric to the native ptrace
where an arch defines a symbol if it has it's own ptrace?
Also when prototyping something like this I was wondering whether we
really want a separate compat function. Lots of the ptrace requests
mostly depend on the target processes abi, not the ptrace caller, so
maybe doing it like s390 and handle both in the same function might
actually be cleaner. Anyway, that's probably something to worry about
later one the arch-specific compat ptrace implementations are gone.
On Thu, Dec 20, 2007 at 03:58:16AM -0800, Roland McGrath wrote:
> +obj-$(CONFIG_PPC64) += ../../../fs/compat_binfmt_elf.o
Building files from another directory is nasty. Please add a
CONFIG_BINFMT_COMPAT_ELF so we can simply build it in fs/
> What's a Red Hat Author? Sorry for the nitpicking, but why don't you
> just use Author like everyone else?
This text is the standard that my employer specifies.
Thanks,
Roland
> Nice, we should have unified the compat ptrace code long ago.
There is still more consolidation that can be done, but this is a first step.
> Any chance you could make the ifdef symetric to the native ptrace
> where an arch defines a symbol if it has it's own ptrace?
That would require touching each and every arch's headers so as
not to break it with the generic changes. I seem to get berated
for that sort of thing more often than asked for it.
> Also when prototyping something like this I was wondering whether we
> really want a separate compat function. Lots of the ptrace requests
> mostly depend on the target processes abi, not the ptrace caller, [...]
That's not really true of any ptrace request that isn't already
wholly arch-dependent, and not true of any majority of those either.
For all of the things that are likely to be handled in generic code
any time soon, it's the caller of ptrace whose natural data formats
reign.
> [...] Anyway, that's probably something to worry about
> later one the arch-specific compat ptrace implementations are gone.
Indeed.
Thanks,
Roland
> On Thu, Dec 20, 2007 at 03:58:16AM -0800, Roland McGrath wrote:
> > +obj-$(CONFIG_PPC64) += ../../../fs/compat_binfmt_elf.o
>
> Building files from another directory is nasty. Please add a
> CONFIG_BINFMT_COMPAT_ELF so we can simply build it in fs/
If that's better, please post the precise Kconfig magic you have in mind to
have it set when it should be.
Thanks,
Roland
On Fri, 2007-12-21 at 00:44 -0800, Roland McGrath wrote:
> > What's a Red Hat Author? Sorry for the nitpicking, but why don't you
> > just use Author like everyone else?
>
> This text is the standard that my employer specifies.
I can't remember seeing anyone else doing this, and there are plenty of
us around.
I usually use:
* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]>
On Fri, 2007-12-21 at 00:44 -0800, Roland McGrath wrote:
> > What's a Red Hat Author? Sorry for the nitpicking, but why don't you
> > just use Author like everyone else?
>
> This text is the standard that my employer specifies.
I have never heard of this.
--
dwmw2
On Thursday 20 December 2007, Roland McGrath wrote:
> This adds fs/compat_binfmt_elf.c, a wrapper around fs/binfmt_elf.c for
> 32-bit ELF support on 64-bit kernels. ?It can replace all the hand-rolled
> versions of this that each 32/64 arch has, which are all about the same.
Great stuff! I've attempted to do this a few times over the past years,
but could never get my head around it. One more bit of broken compat
code gone from the architectures!
Arnd <><
On Fri, Dec 21, 2007 at 12:56:09AM -0800, Roland McGrath wrote:
> > On Thu, Dec 20, 2007 at 03:58:16AM -0800, Roland McGrath wrote:
> > > +obj-$(CONFIG_PPC64) += ../../../fs/compat_binfmt_elf.o
> >
> > Building files from another directory is nasty. Please add a
> > CONFIG_BINFMT_COMPAT_ELF so we can simply build it in fs/
>
> If that's better, please post the precise Kconfig magic you have in mind to
> have it set when it should be.
>
Just taking a stab that hch means,
config BINFMT_COMPAT_ELF
def_bool n
depends on 64BIT
and then in arch/powerpc/Kconfig
config COMPAT
bool
default y if PPC64
select BINFMT_COMPAT_ELF
or somesuch.
Regards, Kyle
On Friday 21 December 2007, Kyle McMartin wrote:
> Just taking a stab that hch means,
>
> config BINFMT_COMPAT_ELF
> ????????def_bool n
> ????????depends on 64BIT
>
I'd call it COMPAT_BINFMT_ELF, for consistency with the file name.
Also, the definition and the depends are redundant if you expect the
option to be autoselected. You can do either of
config COMPAT_BINFMT_ELF
bool
or
config COMPAT_BINFMT_ELF
def_bool y
depends on COMPAT
The second option makes sense at the point where all architectures with
compat code are using the same compat_binfmt_elf code.
Arnd <><
On Fri, Dec 21, 2007 at 12:51:06PM -0500, Kyle McMartin wrote:
> On Fri, Dec 21, 2007 at 12:56:09AM -0800, Roland McGrath wrote:
> > > On Thu, Dec 20, 2007 at 03:58:16AM -0800, Roland McGrath wrote:
> > > > +obj-$(CONFIG_PPC64) += ../../../fs/compat_binfmt_elf.o
> > >
> > > Building files from another directory is nasty. Please add a
> > > CONFIG_BINFMT_COMPAT_ELF so we can simply build it in fs/
> >
> > If that's better, please post the precise Kconfig magic you have in mind to
> > have it set when it should be.
Kyle made a proposal but I like to get in to the party too...
> >
>
> Just taking a stab that hch means,
>
> config BINFMT_COMPAT_ELF
> def_bool n
> depends on 64BIT
>
> and then in arch/powerpc/Kconfig
>
> config COMPAT
> bool
> default y if PPC64
> select BINFMT_COMPAT_ELF
>
> or somesuch.
We recently discussed a common prefix for the selctable symbols
and consensus pointed out "HAVE_" so let us try to use it.
I did not quite understand the "depends on 64BIT" in Kyles example.
Does we really want to use compat_binfmt_elf for all archs that
define 64BIT? Anyway I added this in the example below.
fs/Makefile:
obj-$(COMPAT_BINFMT_ELF) += compat_binfmt_elf.o
fs/Kconfig:
config COMPAT_BINFMT_ELF
depends on HAVE_COMPAT_BINFMT_ELF || 64BIT
# COMPAT_BINFMT_ELF must be selected when an
# architecture supoorts ...
config HAVE_COMPAT_BINFMT_ELF
arch/powerpc/Kconfig:
config COMPAT
bool
default PPC64
select HAVE_COMPAT_BINFMT_ELF
In the example above the extra indirection:
HAVE_COMPAT_BINFMT_ELF => COMPAT_BNFMT_ELF is not really needed
but tomorrow when we add another "depends on" to COMPAT_INFMT_ELF
it is needed to avoid the misbehaving select that just ignore the
dependencies and select the symbol anyway.
Sam
This patch replaces my 2007-12-20 patch by the same title, and has to take
its place in the order of applying that whole series.
Further testing revealed a bug that resulted in regset-based core dumps of
32-bit processes on 64-bit kernels having r0..r3 cleared to zero. The fix
(interdiff from original patch) is a trivial pair of lines in two places.
Thanks,
Roland
---
This extends task_user_regset_view CONFIG_PPC64 with support for the 32-bit
view of register state, compatible with what a CONFIG_PPC32 kernel provides.
This will enable generic machine-independent code to access user-mode
threads' registers for debugging and dumping.
Signed-off-by: Roland McGrath <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 162 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 162 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index eb00274..60de9ee 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -501,8 +501,170 @@ static const struct user_regset_view user_ppc_native_view = {
.regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
};
+#ifdef CONFIG_PPC64
+#include <linux/compat.h>
+
+static int gpr32_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const unsigned long *regs = &target->thread.regs->gpr[0];
+ compat_ulong_t *k = kbuf;
+ compat_ulong_t __user *u = ubuf;
+ compat_ulong_t reg;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+
+ pos /= sizeof(reg);
+ count /= sizeof(reg);
+
+ if (kbuf)
+ for (; count > 0 && pos < PT_MSR; --count)
+ *k++ = regs[pos++];
+ else
+ for (; count > 0 && pos < PT_MSR; --count)
+ if (__put_user((compat_ulong_t) regs[pos++], u++))
+ return -EFAULT;
+
+ if (count > 0 && pos == PT_MSR) {
+ reg = get_user_msr(target);
+ if (kbuf)
+ *k++ = reg;
+ else if (__put_user(reg, u++))
+ return -EFAULT;
+ ++pos;
+ --count;
+ }
+
+ if (kbuf)
+ for (; count > 0 && pos < PT_REGS_COUNT; --count)
+ *k++ = regs[pos++];
+ else
+ for (; count > 0 && pos < PT_REGS_COUNT; --count)
+ if (__put_user((compat_ulong_t) regs[pos++], u++))
+ return -EFAULT;
+
+ kbuf = k;
+ ubuf = u;
+ pos *= sizeof(reg);
+ count *= sizeof(reg);
+ return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ PT_REGS_COUNT * sizeof(reg), -1);
+}
+
+static int gpr32_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned long *regs = &target->thread.regs->gpr[0];
+ const compat_ulong_t *k = kbuf;
+ const compat_ulong_t __user *u = ubuf;
+ compat_ulong_t reg;
+
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+
+ pos /= sizeof(reg);
+ count /= sizeof(reg);
+
+ if (kbuf)
+ for (; count > 0 && pos < PT_MSR; --count)
+ regs[pos++] = *k++;
+ else
+ for (; count > 0 && pos < PT_MSR; --count) {
+ if (__get_user(reg, u++))
+ return -EFAULT;
+ regs[pos++] = reg;
+ }
+
+
+ if (count > 0 && pos == PT_MSR) {
+ if (kbuf)
+ reg = *k++;
+ else if (__get_user(reg, u++))
+ return -EFAULT;
+ set_user_msr(target, reg);
+ ++pos;
+ --count;
+ }
+
+ if (kbuf)
+ for (; count > 0 && pos <= PT_MAX_PUT_REG; --count)
+ regs[pos++] = *k++;
+ else
+ for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) {
+ if (__get_user(reg, u++))
+ return -EFAULT;
+ regs[pos++] = reg;
+ }
+
+ if (count > 0 && pos == PT_TRAP) {
+ if (kbuf)
+ reg = *k++;
+ else if (__get_user(reg, u++))
+ return -EFAULT;
+ set_user_trap(target, reg);
+ ++pos;
+ --count;
+ }
+
+ kbuf = k;
+ ubuf = u;
+ pos *= sizeof(reg);
+ count *= sizeof(reg);
+ return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ (PT_TRAP + 1) * sizeof(reg), -1);
+}
+
+/*
+ * These are the regset flavors matching the CONFIG_PPC32 native set.
+ */
+static const struct user_regset compat_regsets[] = {
+ [REGSET_GPR] = {
+ .core_note_type = NT_PRSTATUS, .n = ELF_NGREG,
+ .size = sizeof(compat_long_t), .align = sizeof(compat_long_t),
+ .get = gpr32_get, .set = gpr32_set
+ },
+ [REGSET_FPR] = {
+ .core_note_type = NT_PRFPREG, .n = ELF_NFPREG,
+ .size = sizeof(double), .align = sizeof(double),
+ .get = fpr_get, .set = fpr_set
+ },
+#ifdef CONFIG_ALTIVEC
+ [REGSET_VMX] = {
+ .core_note_type = NT_PPC_VMX, .n = 34,
+ .size = sizeof(vector128), .align = sizeof(vector128),
+ .active = vr_active, .get = vr_get, .set = vr_set
+ },
+#endif
+#ifdef CONFIG_SPE
+ [REGSET_SPE] = {
+ .n = 35,
+ .size = sizeof(u32), .align = sizeof(u32),
+ .active = evr_active, .get = evr_get, .set = evr_set
+ },
+#endif
+};
+
+static const struct user_regset_view user_ppc_compat_view = {
+ .name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI,
+ .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets)
+};
+#endif /* CONFIG_PPC64 */
+
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
+#ifdef CONFIG_PPC64
+ if (test_tsk_thread_flag(task, TIF_32BIT))
+ return &user_ppc_compat_view;
+#endif
return &user_ppc_native_view;
}