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/module.h> 30#include <linux/syscalls.h> 31 32#include <asm/uaccess.h> 33 34#include "spufs.h" 35 36static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer, 37 size_t size, loff_t *off) 38{ 39 u64 data; 40 int ret; 41 42 if (spufs_coredump_read[num].read) 43 return spufs_coredump_read[num].read(ctx, buffer, size, off); 44 45 data = spufs_coredump_read[num].get(ctx); 46 ret = snprintf(buffer, size, "0x%.16llx", data); 47 if (ret >= size) 48 return size; 49 return ++ret; /* count trailing NULL */ 50} 51 52/* 53 * These are the only things you should do on a core-file: use only these 54 * functions to write out all the necessary info. 55 */ 56static int spufs_dump_write(struct file *file, const void *addr, int nr, loff_t *foffset) 57{ 58 unsigned long limit = rlimit(RLIMIT_CORE); 59 ssize_t written; 60 61 if (*foffset + nr > limit) 62 return -EIO; 63 64 written = file->f_op->write(file, addr, nr, &file->f_pos); 65 *foffset += written; 66 67 if (written != nr) 68 return -EIO; 69 70 return 0; 71} 72 73static int spufs_dump_align(struct file *file, char *buf, loff_t new_off, 74 loff_t *foffset) 75{ 76 int rc, size; 77 78 size = min((loff_t)PAGE_SIZE, new_off - *foffset); 79 memset(buf, 0, size); 80 81 rc = 0; 82 while (rc == 0 && new_off > *foffset) { 83 size = min((loff_t)PAGE_SIZE, new_off - *foffset); 84 rc = spufs_dump_write(file, buf, size, foffset); 85 } 86 87 return rc; 88} 89 90static int spufs_ctx_note_size(struct spu_context *ctx, int dfd) 91{ 92 int i, sz, total = 0; 93 char *name; 94 char fullname[80]; 95 96 for (i = 0; spufs_coredump_read[i].name != NULL; i++) { 97 name = spufs_coredump_read[i].name; 98 sz = spufs_coredump_read[i].size; 99 100 sprintf(fullname, "SPU/%d/%s", dfd, name); 101 102 total += sizeof(struct elf_note); 103 total += roundup(strlen(fullname) + 1, 4); 104 total += roundup(sz, 4); 105 } 106 107 return total; 108} 109 110/* 111 * The additional architecture-specific notes for Cell are various 112 * context files in the spu context. 113 * 114 * This function iterates over all open file descriptors and sees 115 * if they are a directory in spufs. In that case we use spufs 116 * internal functionality to dump them without needing to actually 117 * open the files. 118 */ 119static struct spu_context *coredump_next_context(int *fd) 120{ 121 struct fdtable *fdt = files_fdtable(current->files); 122 struct file *file; 123 struct spu_context *ctx = NULL; 124 125 for (; *fd < fdt->max_fds; (*fd)++) { 126 if (!FD_ISSET(*fd, fdt->open_fds)) 127 continue; 128 129 file = fcheck(*fd); 130 131 if (!file || file->f_op != &spufs_context_fops) 132 continue; 133 134 ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx; 135 if (ctx->flags & SPU_CREATE_NOSCHED) 136 continue; 137 138 break; 139 } 140 141 return ctx; 142} 143 144int spufs_coredump_extra_notes_size(void) 145{ 146 struct spu_context *ctx; 147 int size = 0, rc, fd; 148 149 fd = 0; 150 while ((ctx = coredump_next_context(&fd)) != NULL) { 151 rc = spu_acquire_saved(ctx); 152 if (rc) 153 break; 154 rc = spufs_ctx_note_size(ctx, fd); 155 spu_release_saved(ctx); 156 if (rc < 0) 157 break; 158 159 size += rc; 160 161 /* start searching the next fd next time */ 162 fd++; 163 } 164 165 return size; 166} 167 168static int spufs_arch_write_note(struct spu_context *ctx, int i, 169 struct file *file, int dfd, loff_t *foffset) 170{ 171 loff_t pos = 0; 172 int sz, rc, nread, total = 0; 173 const int bufsz = PAGE_SIZE; 174 char *name; 175 char fullname[80], *buf; 176 struct elf_note en; 177 178 buf = (void *)get_zeroed_page(GFP_KERNEL); 179 if (!buf) 180 return -ENOMEM; 181 182 name = spufs_coredump_read[i].name; 183 sz = spufs_coredump_read[i].size; 184 185 sprintf(fullname, "SPU/%d/%s", dfd, name); 186 en.n_namesz = strlen(fullname) + 1; 187 en.n_descsz = sz; 188 en.n_type = NT_SPU; 189 190 rc = spufs_dump_write(file, &en, sizeof(en), foffset); 191 if (rc) 192 goto out; 193 194 rc = spufs_dump_write(file, fullname, en.n_namesz, foffset); 195 if (rc) 196 goto out; 197 198 rc = spufs_dump_align(file, buf, roundup(*foffset, 4), foffset); 199 if (rc) 200 goto out; 201 202 do { 203 nread = do_coredump_read(i, ctx, buf, bufsz, &pos); 204 if (nread > 0) { 205 rc = spufs_dump_write(file, buf, nread, foffset); 206 if (rc) 207 goto out; 208 total += nread; 209 } 210 } while (nread == bufsz && total < sz); 211 212 if (nread < 0) { 213 rc = nread; 214 goto out; 215 } 216 217 rc = spufs_dump_align(file, buf, roundup(*foffset - total + sz, 4), 218 foffset); 219 220out: 221 free_page((unsigned long)buf); 222 return rc; 223} 224 225int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset) 226{ 227 struct spu_context *ctx; 228 int fd, j, rc; 229 230 fd = 0; 231 while ((ctx = coredump_next_context(&fd)) != NULL) { 232 rc = spu_acquire_saved(ctx); 233 if (rc) 234 return rc; 235 236 for (j = 0; spufs_coredump_read[j].name != NULL; j++) { 237 rc = spufs_arch_write_note(ctx, j, file, fd, foffset); 238 if (rc) { 239 spu_release_saved(ctx); 240 return rc; 241 } 242 } 243 244 spu_release_saved(ctx); 245 246 /* start searching the next fd next time */ 247 fd++; 248 } 249 250 return 0; 251} 252