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