Subject: [PATCH 2.6.18] coredump: Add SPU elf notes to coredump. From: Dwayne Grant McConnell 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, 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. Signed-off-by: Dwayne Grant McConnell Signed-off-by: Arnd Bergmann This is just preliminary, the file handling will have to be done differently to avoid calling syscalls from the core dump path, and we need to watch out for nonschedulable contexts. It's not clear yet how the best integration into the common core dump code should look like. Arnd <>< Index: linux-2.6/arch/powerpc/platforms/cell/spu_coredump.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/platforms/cell/spu_coredump.c @@ -0,0 +1,251 @@ +/* + * SPU core dump code + * + * (C) Copyright 2006 IBM Corp. + * + * Author: Dwayne Grant McConnell + * + * 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 +#include +#include +#include +#include + +#define NUM_SPU_NOTES 18 + +struct spu_ctx_list { + struct list_head list; + int dfd; + int memsize; /* in bytes */ +}; + +static LIST_HEAD(spu_ctx_list); +struct spu_file { + char *name; + int size; /* in bytes */ +}; +static struct spu_file spu_files[NUM_SPU_NOTES] = { + { "regs", 2048, }, /* binary: 128-bit * 128 */ + { "fpcr", 11, }, /* binary: 128-bit */ + { "lslr", 11, }, /* ascii: 0x + 8 bytes + nl */ + { "decr", 11, }, /* ascii: 0x + 8 bytes + nl */ + { "decr_status", 11, }, /* ascii: 0x + 8 bytes + nl */ + { "mem", 262144, }, /* binary: 256 Kb (max) */ + { "signal1", 4, }, /* binary: 32-bit */ + { "signal1_type", 2, }, /* ascii: 1 byte + nl */ + { "signal2", 4, }, /* binary: 32-bit */ + { "signal2_type", 2, }, /* ascii: 1 byte + nl */ + { "event_mask", 11, }, /* ascii: 0x + 8 bytes + nl */ + { "event_status", 11, }, /* ascii: 0x + 8 bytes + nl */ + { "mbox_info", 4, }, /* binary: 32-bit */ + { "ibox_info", 4, }, /* binary: 32-bit */ + { "wbox_info", 16, }, /* binary: 32-bit * 4 (max) */ + { "dma_info", 552, }, /* binary: 64-bit * 69 */ + { "proxydma_info", 280, }, /* binary: 64-bit * 35 */ + { "object-id", 11, }, /* ascii: 0x + 8 bytes + nl */ +}; + +/* + * 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 dump_write(struct file *file, const void *addr, int nr) +{ + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; +} + +static int 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 int spu_get_memsize(int dfd) +{ + int fd, rc, cnt, memsize; + unsigned long long lslr; + char buf[12]; + + fd = sys_openat(dfd, "lslr", O_RDONLY, 0400); + if (fd < 0) + return 0; + rc = sys_read(fd, buf, 12); + if (rc < 0) + return 0; + buf[rc - 1] = '\0'; + rc = sys_close(fd); + cnt = sscanf(buf, "0x%llx", &lslr); + if (cnt != 1) + return 0; + memsize = lslr + 1; + + printk("%d, %d\n", dfd, memsize); + return memsize; +} + +static int spu_ctx_note_size(struct spu_ctx_list *tmp) +{ + int dfd, memsize, i, sz, total = 0; + char *name; + char fullname[80]; + + dfd = tmp->dfd; + memsize = tmp->memsize; + + for (i = 0; i < NUM_SPU_NOTES; i++) { + name = spu_files[i].name; + sz = spu_files[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; +} + +int spu_notes_size(void) +{ + int i, j, sz = 0, error = 0; + struct fdtable *fdt; + + /* Find spu contexts which are directories in the spufs filesystem. */ + j = 0; + fdt = files_fdtable(current->files); + for (;;) { + unsigned long set; + i = j * __NFDBITS; + if (i >= fdt->max_fdset || i >= fdt->max_fds) + break; + set = fdt->open_fds->fds_bits[j++]; + while (set) { + if ((set & 1) && fdt->fd[i] && fdt->fd[i]->f_dentry && + fdt->fd[i]->f_dentry->d_inode) { + struct file *fp = fdt->fd[i]; + struct dentry *dp = fp->f_dentry; + struct inode *ip = dp->d_inode; + if (S_ISDIR(ip->i_mode)) { + struct spu_ctx_list *tmp; + tmp = kzalloc(sizeof(*tmp), + GFP_ATOMIC); + if (!tmp) { + error = 1; + goto cleanup; + } + INIT_LIST_HEAD(&tmp->list); + tmp->dfd = i; + tmp->memsize = spu_get_memsize(i); + sz = spu_ctx_note_size(tmp); + list_add(&tmp->list, &spu_ctx_list); + } + } + i++; + set >>= 1; + } + } + +cleanup: + return sz; +} + +#define DUMP_WRITE_RET(addr, nr) \ + do { if (!dump_write(file, (addr), (nr))) return; } while(0) +#define DUMP_SEEK_RET(off) \ + do { if (!dump_seek(file, (off))) return; } while(0) + +static void spu_write_note(struct spu_ctx_list *ctx, int i, struct file *file) +{ + int sz, dfd, fd, mem, rc, first = 1, last = 0, total = 0; + char *name; + char fullname[80], buf[4096]; + struct elf_note en; + + dfd = ctx->dfd; + name = spu_files[i].name; + sz = spu_files[i].size; + + if (!strcmp(name, "mem")) + mem = 1; + else + mem = 0; + + fd = sys_openat(dfd, name, O_RDONLY, 0400); + if (fd < 0) + goto out; + sprintf(fullname, "SPU/%d/%s", dfd, name); + do { + rc = sys_read(fd, buf, sizeof buf); + total += rc; + if (rc < 0) + goto out; + if (rc < sizeof buf) + last = 1; + if (!rc) + continue; + if (first) { + en.n_namesz = strlen(fullname) + 1; + en.n_descsz = mem ? ctx->memsize : sz; + en.n_type = NT_SPU; + + DUMP_WRITE_RET(&en, sizeof(en)); + DUMP_WRITE_RET(fullname, en.n_namesz); + + DUMP_SEEK_RET(roundup((unsigned long)file->f_pos, 4)); + } + DUMP_WRITE_RET(buf, rc); + if (first || last) { + DUMP_SEEK_RET(roundup((unsigned long)file->f_pos, 4)); + first = 0; + } + } while (rc > 0 && total < sizeof buf); + +out: + if (fd >= 0) + rc = sys_close(fd); +} + +void spu_write_notes(struct file *file) +{ + int i, j; + struct list_head *t; + + i = 0; + list_for_each(t, &spu_ctx_list) { + struct spu_ctx_list *tmp; + + tmp = list_entry(t, struct spu_ctx_list, list); + for (j = 0; j < NUM_SPU_NOTES; j++) + spu_write_note(tmp, j, file); + i++; + } + + while (!list_empty(&spu_ctx_list)) { + struct list_head *tmp = spu_ctx_list.next; + list_del(tmp); + kfree(list_entry(tmp, struct spu_ctx_list, list)); + } +} Index: linux-2.6/fs/binfmt_elf.c =================================================================== --- linux-2.6.orig/fs/binfmt_elf.c +++ linux-2.6/fs/binfmt_elf.c @@ -43,6 +43,10 @@ #include #include +#ifdef ELF_CORE_WRITE_EXTRA_NOTES + +#endif + static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); static int load_elf_library(struct file *); static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int); @@ -1582,6 +1586,10 @@ static int elf_core_dump(long signr, str sz += thread_status_size; +#ifdef ELF_CORE_WRITE_EXTRA_NOTES + sz += ELF_CORE_EXTRA_NOTES_SIZE; +#endif + fill_elf_note_phdr(&phdr, sz, offset); offset += sz; DUMP_WRITE(&phdr, sizeof(phdr)); @@ -1622,6 +1630,10 @@ static int elf_core_dump(long signr, str if (!writenote(notes + i, file, &foffset)) goto end_coredump; +#ifdef ELF_CORE_WRITE_EXTRA_NOTES + ELF_CORE_WRITE_EXTRA_NOTES; +#endif + /* write out the thread status notes section */ list_for_each(t, &thread_list) { struct elf_thread_status *tmp = Index: linux-2.6/include/asm-powerpc/elf.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/elf.h +++ linux-2.6/include/asm-powerpc/elf.h @@ -411,4 +411,17 @@ do { \ /* Keep this the last entry. */ #define R_PPC64_NUM 107 +#ifdef CONFIG_PPC_CELL +/* Notes used in ET_CORE. Note name is "SPU//". */ +#define NT_SPU 1 + +int spu_notes_size(void); + +#define ELF_CORE_EXTRA_NOTES_SIZE spu_notes_size() + +void spu_write_notes(struct file *file); + +#define ELF_CORE_WRITE_EXTRA_NOTES spu_write_notes(file) +#endif /* CONFIG_PPC_CELL */ + #endif /* _ASM_POWERPC_ELF_H */ Index: linux-2.6/arch/powerpc/platforms/cell/Makefile =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/Makefile +++ linux-2.6/arch/powerpc/platforms/cell/Makefile @@ -11,5 +11,6 @@ spufs-modular-$(CONFIG_SPU_FS) += spu_s spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ + spu_coredump.o \ $(spufs-modular-m) \ $(spu-priv1-y) spufs/