1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * SPU core dump code |
4 | * |
5 | * (C) Copyright 2006 IBM Corp. |
6 | * |
7 | * Author: Dwayne Grant McConnell <decimal@us.ibm.com> |
8 | */ |
9 | |
10 | #include <linux/elf.h> |
11 | #include <linux/file.h> |
12 | #include <linux/fdtable.h> |
13 | #include <linux/fs.h> |
14 | #include <linux/gfp.h> |
15 | #include <linux/list.h> |
16 | #include <linux/syscalls.h> |
17 | #include <linux/coredump.h> |
18 | #include <linux/binfmts.h> |
19 | |
20 | #include <linux/uaccess.h> |
21 | |
22 | #include "spufs.h" |
23 | |
24 | static int spufs_ctx_note_size(struct spu_context *ctx, int dfd) |
25 | { |
26 | int i, sz, total = 0; |
27 | char *name; |
28 | char fullname[80]; |
29 | |
30 | for (i = 0; spufs_coredump_read[i].name != NULL; i++) { |
31 | name = spufs_coredump_read[i].name; |
32 | sz = spufs_coredump_read[i].size; |
33 | |
34 | sprintf(buf: fullname, fmt: "SPU/%d/%s" , dfd, name); |
35 | |
36 | total += sizeof(struct elf_note); |
37 | total += roundup(strlen(fullname) + 1, 4); |
38 | total += roundup(sz, 4); |
39 | } |
40 | |
41 | return total; |
42 | } |
43 | |
44 | static int match_context(const void *v, struct file *file, unsigned fd) |
45 | { |
46 | struct spu_context *ctx; |
47 | if (file->f_op != &spufs_context_fops) |
48 | return 0; |
49 | ctx = SPUFS_I(file_inode(file))->i_ctx; |
50 | if (ctx->flags & SPU_CREATE_NOSCHED) |
51 | return 0; |
52 | return fd + 1; |
53 | } |
54 | |
55 | /* |
56 | * The additional architecture-specific notes for Cell are various |
57 | * context files in the spu context. |
58 | * |
59 | * This function iterates over all open file descriptors and sees |
60 | * if they are a directory in spufs. In that case we use spufs |
61 | * internal functionality to dump them without needing to actually |
62 | * open the files. |
63 | */ |
64 | /* |
65 | * descriptor table is not shared, so files can't change or go away. |
66 | */ |
67 | static struct spu_context *coredump_next_context(int *fd) |
68 | { |
69 | struct spu_context *ctx = NULL; |
70 | struct file *file; |
71 | int n = iterate_fd(current->files, *fd, match_context, NULL); |
72 | if (!n) |
73 | return NULL; |
74 | *fd = n - 1; |
75 | |
76 | rcu_read_lock(); |
77 | file = lookup_fdget_rcu(fd: *fd); |
78 | rcu_read_unlock(); |
79 | if (file) { |
80 | ctx = SPUFS_I(file_inode(file))->i_ctx; |
81 | get_spu_context(ctx); |
82 | fput(file); |
83 | } |
84 | |
85 | return ctx; |
86 | } |
87 | |
88 | int (void) |
89 | { |
90 | struct spu_context *ctx; |
91 | int size = 0, rc, fd; |
92 | |
93 | fd = 0; |
94 | while ((ctx = coredump_next_context(fd: &fd)) != NULL) { |
95 | rc = spu_acquire_saved(ctx); |
96 | if (rc) { |
97 | put_spu_context(ctx); |
98 | break; |
99 | } |
100 | |
101 | rc = spufs_ctx_note_size(ctx, dfd: fd); |
102 | spu_release_saved(ctx); |
103 | if (rc < 0) { |
104 | put_spu_context(ctx); |
105 | break; |
106 | } |
107 | |
108 | size += rc; |
109 | |
110 | /* start searching the next fd next time */ |
111 | fd++; |
112 | put_spu_context(ctx); |
113 | } |
114 | |
115 | return size; |
116 | } |
117 | |
118 | static int spufs_arch_write_note(struct spu_context *ctx, int i, |
119 | struct coredump_params *cprm, int dfd) |
120 | { |
121 | size_t sz = spufs_coredump_read[i].size; |
122 | char fullname[80]; |
123 | struct elf_note en; |
124 | int ret; |
125 | |
126 | sprintf(buf: fullname, fmt: "SPU/%d/%s" , dfd, spufs_coredump_read[i].name); |
127 | en.n_namesz = strlen(fullname) + 1; |
128 | en.n_descsz = sz; |
129 | en.n_type = NT_SPU; |
130 | |
131 | if (!dump_emit(cprm, addr: &en, nr: sizeof(en))) |
132 | return -EIO; |
133 | if (!dump_emit(cprm, addr: fullname, nr: en.n_namesz)) |
134 | return -EIO; |
135 | if (!dump_align(cprm, align: 4)) |
136 | return -EIO; |
137 | |
138 | if (spufs_coredump_read[i].dump) { |
139 | ret = spufs_coredump_read[i].dump(ctx, cprm); |
140 | if (ret < 0) |
141 | return ret; |
142 | } else { |
143 | char buf[32]; |
144 | |
145 | ret = snprintf(buf, size: sizeof(buf), fmt: "0x%.16llx" , |
146 | spufs_coredump_read[i].get(ctx)); |
147 | if (ret >= sizeof(buf)) |
148 | return sizeof(buf); |
149 | |
150 | /* count trailing the NULL: */ |
151 | if (!dump_emit(cprm, addr: buf, nr: ret + 1)) |
152 | return -EIO; |
153 | } |
154 | |
155 | dump_skip_to(cprm, roundup(cprm->pos - ret + sz, 4)); |
156 | return 0; |
157 | } |
158 | |
159 | int (struct coredump_params *cprm) |
160 | { |
161 | struct spu_context *ctx; |
162 | int fd, j, rc; |
163 | |
164 | fd = 0; |
165 | while ((ctx = coredump_next_context(fd: &fd)) != NULL) { |
166 | rc = spu_acquire_saved(ctx); |
167 | if (rc) |
168 | return rc; |
169 | |
170 | for (j = 0; spufs_coredump_read[j].name != NULL; j++) { |
171 | rc = spufs_arch_write_note(ctx, i: j, cprm, dfd: fd); |
172 | if (rc) { |
173 | spu_release_saved(ctx); |
174 | return rc; |
175 | } |
176 | } |
177 | |
178 | spu_release_saved(ctx); |
179 | |
180 | /* start searching the next fd next time */ |
181 | fd++; |
182 | } |
183 | |
184 | return 0; |
185 | } |
186 | |