Commit bf1ab978 authored by Dwayne Grant McConnell's avatar Dwayne Grant McConnell Committed by Paul Mackerras

[POWERPC] coredump: Add SPU elf notes to coredump.

This patch adds SPU elf notes to the coredump. It creates a separate note
for each of /regs, /fpcr, /lslr, /decr, /decr_status, /mem, /signal1,
/signal1_type, /signal2, /signal2_type, /event_mask, /event_status,
/mbox_info, /ibox_info, /wbox_info, /dma_info, /proxydma_info, /object-id.

A new macro, ARCH_HAVE_EXTRA_NOTES, was created for architectures to
specify they have extra elf core notes.

A new macro, ELF_CORE_EXTRA_NOTES_SIZE, was created so the size of the
additional notes could be calculated and added to the notes phdr entry.

A new macro, ELF_CORE_WRITE_EXTRA_NOTES, was created so the new notes
would be written after the existing notes.

The SPU coredump code resides in spufs. Stub functions are provided in the
kernel which are hooked into the spufs code which does the actual work via
register_arch_coredump_calls().

A new set of __spufs_<file>_read/get() functions was provided to allow the
coredump code to read from the spufs files without having to lock the
SPU context for each file read from.

Cc: <linux-arch@vger.kernel.org>
Signed-off-by: default avatarDwayne Grant McConnell <decimal@us.ibm.com>
Signed-off-by: default avatarArnd Bergmann <arnd.bergmann@de.ibm.com>
parent 9309180f
...@@ -15,5 +15,6 @@ spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o ...@@ -15,5 +15,6 @@ spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o
spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o
obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
spu_coredump.o \
$(spufs-modular-m) \ $(spufs-modular-m) \
$(spu-priv1-y) spufs/ $(spu-priv1-y) spufs/
/*
* SPU core dump code
*
* (C) Copyright 2006 IBM Corp.
*
* Author: Dwayne Grant McConnell <decimal@us.ibm.com>
*
* 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/file.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <asm/spu.h>
static struct spu_coredump_calls spu_coredump_calls;
static DEFINE_MUTEX(spu_coredump_mutex);
int arch_notes_size(void)
{
long ret;
struct module *owner = spu_coredump_calls.owner;
ret = -ENOSYS;
mutex_lock(&spu_coredump_mutex);
if (owner && try_module_get(owner)) {
ret = spu_coredump_calls.arch_notes_size();
module_put(owner);
}
mutex_unlock(&spu_coredump_mutex);
return ret;
}
void arch_write_notes(struct file *file)
{
struct module *owner = spu_coredump_calls.owner;
mutex_lock(&spu_coredump_mutex);
if (owner && try_module_get(owner)) {
spu_coredump_calls.arch_write_notes(file);
module_put(owner);
}
mutex_unlock(&spu_coredump_mutex);
}
int register_arch_coredump_calls(struct spu_coredump_calls *calls)
{
if (spu_coredump_calls.owner)
return -EBUSY;
mutex_lock(&spu_coredump_mutex);
spu_coredump_calls.arch_notes_size = calls->arch_notes_size;
spu_coredump_calls.arch_write_notes = calls->arch_write_notes;
spu_coredump_calls.owner = calls->owner;
mutex_unlock(&spu_coredump_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(register_arch_coredump_calls);
void unregister_arch_coredump_calls(struct spu_coredump_calls *calls)
{
BUG_ON(spu_coredump_calls.owner != calls->owner);
mutex_lock(&spu_coredump_mutex);
spu_coredump_calls.owner = NULL;
mutex_unlock(&spu_coredump_mutex);
}
EXPORT_SYMBOL_GPL(unregister_arch_coredump_calls);
obj-y += switch.o obj-y += switch.o
obj-$(CONFIG_SPU_FS) += spufs.o obj-$(CONFIG_SPU_FS) += spufs.o
spufs-y += inode.o file.o context.o syscalls.o spufs-y += inode.o file.o context.o syscalls.o coredump.o
spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o
# Rules to build switch.o with the help of SPU tool chain # Rules to build switch.o with the help of SPU tool chain
......
/*
* SPU core dump code
*
* (C) Copyright 2006 IBM Corp.
*
* Author: Dwayne Grant McConnell <decimal@us.ibm.com>
*
* 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/elf.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>
#include "spufs.h"
struct spufs_ctx_info {
struct list_head list;
int dfd;
int memsize; /* in bytes */
struct spu_context *ctx;
};
static LIST_HEAD(ctx_info_list);
static ssize_t do_coredump_read(int num, struct spu_context *ctx, void __user *buffer,
size_t size, loff_t *off)
{
u64 data;
int ret;
if (spufs_coredump_read[num].read)
return spufs_coredump_read[num].read(ctx, buffer, size, off);
data = spufs_coredump_read[num].get(ctx);
ret = copy_to_user(buffer, &data, 8);
return ret ? -EFAULT : 8;
}
/*
* These are the only things you should do on a core-file: use only these
* functions to write out all the necessary info.
*/
static int spufs_dump_write(struct file *file, const void *addr, int nr)
{
return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
}
static int spufs_dump_seek(struct file *file, loff_t off)
{
if (file->f_op->llseek) {
if (file->f_op->llseek(file, off, 0) != off)
return 0;
} else
file->f_pos = off;
return 1;
}
static void spufs_fill_memsize(struct spufs_ctx_info *ctx_info)
{
struct spu_context *ctx;
unsigned long long lslr;
ctx = ctx_info->ctx;
lslr = ctx->csa.priv2.spu_lslr_RW;
ctx_info->memsize = lslr + 1;
}
static int spufs_ctx_note_size(struct spufs_ctx_info *ctx_info)
{
int dfd, memsize, i, sz, total = 0;
char *name;
char fullname[80];
dfd = ctx_info->dfd;
memsize = ctx_info->memsize;
for (i = 0; spufs_coredump_read[i].name; i++) {
name = spufs_coredump_read[i].name;
sz = spufs_coredump_read[i].size;
sprintf(fullname, "SPU/%d/%s", dfd, name);
total += sizeof(struct elf_note);
total += roundup(strlen(fullname) + 1, 4);
if (!strcmp(name, "mem"))
total += roundup(memsize, 4);
else
total += roundup(sz, 4);
}
return total;
}
static int spufs_add_one_context(struct file *file, int dfd)
{
struct spu_context *ctx;
struct spufs_ctx_info *ctx_info;
int size;
ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx;
if (ctx->flags & SPU_CREATE_NOSCHED)
return 0;
ctx_info = kzalloc(sizeof(*ctx_info), GFP_KERNEL);
if (unlikely(!ctx_info))
return -ENOMEM;
ctx_info->dfd = dfd;
ctx_info->ctx = ctx;
spufs_fill_memsize(ctx_info);
size = spufs_ctx_note_size(ctx_info);
list_add(&ctx_info->list, &ctx_info_list);
return size;
}
/*
* The additional architecture-specific notes for Cell are various
* context files in the spu context.
*
* This function iterates over all open file descriptors and sees
* if they are a directory in spufs. In that case we use spufs
* internal functionality to dump them without needing to actually
* open the files.
*/
static int spufs_arch_notes_size(void)
{
struct fdtable *fdt = files_fdtable(current->files);
int size = 0, fd;
for (fd = 0; fd < fdt->max_fdset && fd < fdt->max_fds; fd++) {
if (FD_ISSET(fd, fdt->open_fds)) {
struct file *file = fcheck(fd);
if (file && file->f_op == &spufs_context_fops) {
int rval = spufs_add_one_context(file, fd);
if (rval < 0)
break;
size += rval;
}
}
}
return size;
}
static void spufs_arch_write_note(struct spufs_ctx_info *ctx_info, int i,
struct file *file)
{
struct spu_context *ctx;
loff_t pos = 0;
int sz, dfd, rc, total = 0;
const int bufsz = 4096;
char *name;
char fullname[80], *buf;
struct elf_note en;
buf = kmalloc(bufsz, GFP_KERNEL);
if (!buf)
return;
dfd = ctx_info->dfd;
name = spufs_coredump_read[i].name;
if (!strcmp(name, "mem"))
sz = ctx_info->memsize;
else
sz = spufs_coredump_read[i].size;
ctx = ctx_info->ctx;
if (!ctx) {
return;
}
sprintf(fullname, "SPU/%d/%s", dfd, name);
en.n_namesz = strlen(fullname) + 1;
en.n_descsz = sz;
en.n_type = NT_SPU;
if (!spufs_dump_write(file, &en, sizeof(en)))
return;
if (!spufs_dump_write(file, fullname, en.n_namesz))
return;
if (!spufs_dump_seek(file, roundup((unsigned long)file->f_pos, 4)))
return;
do {
rc = do_coredump_read(i, ctx, buf, bufsz, &pos);
if (rc > 0) {
if (!spufs_dump_write(file, buf, rc))
return;
total += rc;
}
} while (rc == bufsz && total < sz);
spufs_dump_seek(file, roundup((unsigned long)file->f_pos
- total + sz, 4));
}
static void spufs_arch_write_notes(struct file *file)
{
int j;
struct spufs_ctx_info *ctx_info, *next;
list_for_each_entry_safe(ctx_info, next, &ctx_info_list, list) {
spu_acquire_saved(ctx_info->ctx);
for (j = 0; j < spufs_coredump_num_notes; j++)
spufs_arch_write_note(ctx_info, j, file);
spu_release(ctx_info->ctx);
list_del(&ctx_info->list);
kfree(ctx_info);
}
}
struct spu_coredump_calls spufs_coredump_calls = {
.arch_notes_size = spufs_arch_notes_size,
.arch_write_notes = spufs_arch_write_notes,
.owner = THIS_MODULE,
};
This diff is collapsed.
...@@ -232,6 +232,7 @@ struct file_operations spufs_context_fops = { ...@@ -232,6 +232,7 @@ struct file_operations spufs_context_fops = {
.readdir = dcache_readdir, .readdir = dcache_readdir,
.fsync = simple_sync_file, .fsync = simple_sync_file,
}; };
EXPORT_SYMBOL_GPL(spufs_context_fops);
static int static int
spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
...@@ -647,6 +648,7 @@ static struct file_system_type spufs_type = { ...@@ -647,6 +648,7 @@ static struct file_system_type spufs_type = {
static int __init spufs_init(void) static int __init spufs_init(void)
{ {
int ret; int ret;
ret = -ENOMEM; ret = -ENOMEM;
spufs_inode_cache = kmem_cache_create("spufs_inode_cache", spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
sizeof(struct spufs_inode_info), 0, sizeof(struct spufs_inode_info), 0,
...@@ -662,10 +664,14 @@ static int __init spufs_init(void) ...@@ -662,10 +664,14 @@ static int __init spufs_init(void)
if (ret) if (ret)
goto out_cache; goto out_cache;
ret = register_spu_syscalls(&spufs_calls); ret = register_spu_syscalls(&spufs_calls);
if (ret)
goto out_fs;
ret = register_arch_coredump_calls(&spufs_coredump_calls);
if (ret) if (ret)
goto out_fs; goto out_fs;
spufs_init_isolated_loader(); spufs_init_isolated_loader();
return 0; return 0;
out_fs: out_fs:
unregister_filesystem(&spufs_type); unregister_filesystem(&spufs_type);
...@@ -679,6 +685,7 @@ module_init(spufs_init); ...@@ -679,6 +685,7 @@ module_init(spufs_init);
static void __exit spufs_exit(void) static void __exit spufs_exit(void)
{ {
spu_sched_exit(); spu_sched_exit();
unregister_arch_coredump_calls(&spufs_coredump_calls);
unregister_spu_syscalls(&spufs_calls); unregister_spu_syscalls(&spufs_calls);
unregister_filesystem(&spufs_type); unregister_filesystem(&spufs_type);
kmem_cache_destroy(spufs_inode_cache); kmem_cache_destroy(spufs_inode_cache);
......
...@@ -223,4 +223,15 @@ void spufs_stop_callback(struct spu *spu); ...@@ -223,4 +223,15 @@ void spufs_stop_callback(struct spu *spu);
void spufs_mfc_callback(struct spu *spu); void spufs_mfc_callback(struct spu *spu);
void spufs_dma_callback(struct spu *spu, int type); void spufs_dma_callback(struct spu *spu, int type);
extern struct spu_coredump_calls spufs_coredump_calls;
struct spufs_coredump_reader {
char *name;
ssize_t (*read)(struct spu_context *ctx,
char __user *buffer, size_t size, loff_t *pos);
u64 (*get)(void *data);
size_t size;
};
extern struct spufs_coredump_reader spufs_coredump_read[];
extern int spufs_coredump_num_notes;
#endif #endif
...@@ -1582,6 +1582,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) ...@@ -1582,6 +1582,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
sz += thread_status_size; sz += thread_status_size;
#ifdef ELF_CORE_WRITE_EXTRA_NOTES
sz += ELF_CORE_EXTRA_NOTES_SIZE;
#endif
fill_elf_note_phdr(&phdr, sz, offset); fill_elf_note_phdr(&phdr, sz, offset);
offset += sz; offset += sz;
DUMP_WRITE(&phdr, sizeof(phdr)); DUMP_WRITE(&phdr, sizeof(phdr));
...@@ -1622,6 +1626,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) ...@@ -1622,6 +1626,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
if (!writenote(notes + i, file, &foffset)) if (!writenote(notes + i, file, &foffset))
goto end_coredump; goto end_coredump;
#ifdef ELF_CORE_WRITE_EXTRA_NOTES
ELF_CORE_WRITE_EXTRA_NOTES;
#endif
/* write out the thread status notes section */ /* write out the thread status notes section */
list_for_each(t, &thread_list) { list_for_each(t, &thread_list) {
struct elf_thread_status *tmp = struct elf_thread_status *tmp =
......
...@@ -411,4 +411,17 @@ do { \ ...@@ -411,4 +411,17 @@ do { \
/* Keep this the last entry. */ /* Keep this the last entry. */
#define R_PPC64_NUM 107 #define R_PPC64_NUM 107
#ifdef CONFIG_PPC_CELL
/* Notes used in ET_CORE. Note name is "SPU/<fd>/<filename>". */
#define NT_SPU 1
extern int arch_notes_size(void);
extern void arch_write_notes(struct file *file);
#define ELF_CORE_EXTRA_NOTES_SIZE arch_notes_size()
#define ELF_CORE_WRITE_EXTRA_NOTES arch_write_notes(file)
#define ARCH_HAVE_EXTRA_ELF_NOTES
#endif /* CONFIG_PPC_CELL */
#endif /* _ASM_POWERPC_ELF_H */ #endif /* _ASM_POWERPC_ELF_H */
...@@ -172,6 +172,13 @@ extern struct spufs_calls { ...@@ -172,6 +172,13 @@ extern struct spufs_calls {
struct module *owner; struct module *owner;
} spufs_calls; } spufs_calls;
/* coredump calls implemented in spufs */
struct spu_coredump_calls {
asmlinkage int (*arch_notes_size)(void);
asmlinkage void (*arch_write_notes)(struct file *file);
struct module *owner;
};
/* return status from spu_run, same as in libspe */ /* return status from spu_run, same as in libspe */
#define SPE_EVENT_DMA_ALIGNMENT 0x0008 /*A DMA alignment error */ #define SPE_EVENT_DMA_ALIGNMENT 0x0008 /*A DMA alignment error */
#define SPE_EVENT_SPE_ERROR 0x0010 /*An illegal instruction error*/ #define SPE_EVENT_SPE_ERROR 0x0010 /*An illegal instruction error*/
...@@ -203,6 +210,9 @@ static inline void unregister_spu_syscalls(struct spufs_calls *calls) ...@@ -203,6 +210,9 @@ static inline void unregister_spu_syscalls(struct spufs_calls *calls)
} }
#endif /* MODULE */ #endif /* MODULE */
int register_arch_coredump_calls(struct spu_coredump_calls *calls);
void unregister_arch_coredump_calls(struct spu_coredump_calls *calls);
int spu_add_sysdev_attr(struct sysdev_attribute *attr); int spu_add_sysdev_attr(struct sysdev_attribute *attr);
void spu_remove_sysdev_attr(struct sysdev_attribute *attr); void spu_remove_sysdev_attr(struct sysdev_attribute *attr);
......
...@@ -368,5 +368,12 @@ extern Elf64_Dyn _DYNAMIC []; ...@@ -368,5 +368,12 @@ extern Elf64_Dyn _DYNAMIC [];
#endif #endif
#ifndef ARCH_HAVE_EXTRA_ELF_NOTES
static inline int arch_notes_size(void) { return 0; }
static inline void arch_write_notes(struct file *file) { }
#define ELF_CORE_EXTRA_NOTES_SIZE arch_notes_size()
#define ELF_CORE_WRITE_EXTRA_NOTES arch_write_notes(file)
#endif /* ARCH_HAVE_EXTRA_ELF_NOTES */
#endif /* _LINUX_ELF_H */ #endif /* _LINUX_ELF_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment