1/* 2 * SPU core dump code 3 * 4 * (C) Copyright 2006 IBM Corp. 5 * 6 * Author: Dwayne Grant McConnell <decimal@us.ibm.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23#include <linux/elf.h> 24#include <linux/file.h> 25#include <linux/fdtable.h> 26#include <linux/fs.h> 27#include <linux/gfp.h> 28#include <linux/list.h> 29#include <linux/syscalls.h> 30#include <linux/coredump.h> 31#include <linux/binfmts.h> 32 33#include <asm/uaccess.h> 34 35#include "spufs.h" 36 37static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer, 38 size_t size, loff_t *off) 39{ 40 u64 data; 41 int ret; 42 43 if (spufs_coredump_read[num].read) 44 return spufs_coredump_read[num].read(ctx, buffer, size, off); 45 46 data = spufs_coredump_read[num].get(ctx); 47 ret = snprintf(buffer, size, "0x%.16llx", data); 48 if (ret >= size) 49 return size; 50 return ++ret; /* count trailing NULL */ 51} 52 53static int spufs_ctx_note_size(struct spu_context *ctx, int dfd) 54{ 55 int i, sz, total = 0; 56 char *name; 57 char fullname[80]; 58 59 for (i = 0; spufs_coredump_read[i].name != NULL; i++) { 60 name = spufs_coredump_read[i].name; 61 sz = spufs_coredump_read[i].size; 62 63 sprintf(fullname, "SPU/%d/%s", dfd, name); 64 65 total += sizeof(struct elf_note); 66 total += roundup(strlen(fullname) + 1, 4); 67 total += roundup(sz, 4); 68 } 69 70 return total; 71} 72 73static int match_context(const void *v, struct file *file, unsigned fd) 74{ 75 struct spu_context *ctx; 76 if (file->f_op != &spufs_context_fops) 77 return 0; 78 ctx = SPUFS_I(file_inode(file))->i_ctx; 79 if (ctx->flags & SPU_CREATE_NOSCHED) 80 return 0; 81 return fd + 1; 82} 83 84/* 85 * The additional architecture-specific notes for Cell are various 86 * context files in the spu context. 87 * 88 * This function iterates over all open file descriptors and sees 89 * if they are a directory in spufs. In that case we use spufs 90 * internal functionality to dump them without needing to actually 91 * open the files. 92 */ 93/* 94 * descriptor table is not shared, so files can't change or go away. 95 */ 96static struct spu_context *coredump_next_context(int *fd) 97{ 98 struct file *file; 99 int n = iterate_fd(current->files, *fd, match_context, NULL); 100 if (!n) 101 return NULL; 102 *fd = n - 1; 103 file = fcheck(*fd); 104 return SPUFS_I(file_inode(file))->i_ctx; 105} 106 107int spufs_coredump_extra_notes_size(void) 108{ 109 struct spu_context *ctx; 110 int size = 0, rc, fd; 111 112 fd = 0; 113 while ((ctx = coredump_next_context(&fd)) != NULL) { 114 rc = spu_acquire_saved(ctx); 115 if (rc) 116 break; 117 rc = spufs_ctx_note_size(ctx, fd); 118 spu_release_saved(ctx); 119 if (rc < 0) 120 break; 121 122 size += rc; 123 124 /* start searching the next fd next time */ 125 fd++; 126 } 127 128 return size; 129} 130 131static int spufs_arch_write_note(struct spu_context *ctx, int i, 132 struct coredump_params *cprm, int dfd) 133{ 134 loff_t pos = 0; 135 int sz, rc, total = 0; 136 const int bufsz = PAGE_SIZE; 137 char *name; 138 char fullname[80], *buf; 139 struct elf_note en; 140 141 buf = (void *)get_zeroed_page(GFP_KERNEL); 142 if (!buf) 143 return -ENOMEM; 144 145 name = spufs_coredump_read[i].name; 146 sz = spufs_coredump_read[i].size; 147 148 sprintf(fullname, "SPU/%d/%s", dfd, name); 149 en.n_namesz = strlen(fullname) + 1; 150 en.n_descsz = sz; 151 en.n_type = NT_SPU; 152 153 if (!dump_emit(cprm, &en, sizeof(en))) 154 goto Eio; 155 156 if (!dump_emit(cprm, fullname, en.n_namesz)) 157 goto Eio; 158 159 if (!dump_align(cprm, 4)) 160 goto Eio; 161 162 do { 163 rc = do_coredump_read(i, ctx, buf, bufsz, &pos); 164 if (rc > 0) { 165 if (!dump_emit(cprm, buf, rc)) 166 goto Eio; 167 total += rc; 168 } 169 } while (rc == bufsz && total < sz); 170 171 if (rc < 0) 172 goto out; 173 174 if (!dump_skip(cprm, 175 roundup(cprm->written - total + sz, 4) - cprm->written)) 176 goto Eio; 177out: 178 free_page((unsigned long)buf); 179 return rc; 180Eio: 181 free_page((unsigned long)buf); 182 return -EIO; 183} 184 185int spufs_coredump_extra_notes_write(struct coredump_params *cprm) 186{ 187 struct spu_context *ctx; 188 int fd, j, rc; 189 190 fd = 0; 191 while ((ctx = coredump_next_context(&fd)) != NULL) { 192 rc = spu_acquire_saved(ctx); 193 if (rc) 194 return rc; 195 196 for (j = 0; spufs_coredump_read[j].name != NULL; j++) { 197 rc = spufs_arch_write_note(ctx, j, cprm, fd); 198 if (rc) { 199 spu_release_saved(ctx); 200 return rc; 201 } 202 } 203 204 spu_release_saved(ctx); 205 206 /* start searching the next fd next time */ 207 fd++; 208 } 209 210 return 0; 211} 212