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