xref: /freebsd/sys/powerpc/powerpc/dump_machdep.c (revision 3b3a8eb937bf8045231e8364bfd1b94cd4a95979)
1 /*-
2  * Copyright (c) 2002 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include "opt_watchdog.h"
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/cons.h>
36 #include <sys/kernel.h>
37 #include <sys/kerneldump.h>
38 #include <sys/sysctl.h>
39 #ifdef SW_WATCHDOG
40 #include <sys/watchdog.h>
41 #endif
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44 #include <machine/elf.h>
45 #include <machine/md_var.h>
46 
47 CTASSERT(sizeof(struct kerneldumpheader) == 512);
48 
49 /*
50  * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
51  * is to protect us from metadata and to protect metadata from us.
52  */
53 #define	SIZEOF_METADATA		(64*1024)
54 
55 #define	MD_ALIGN(x)	(((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
56 #define	DEV_ALIGN(x)	(((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
57 
58 typedef int callback_t(struct pmap_md *, int, void *);
59 
60 static struct kerneldumpheader kdh;
61 static off_t dumplo, fileofs;
62 
63 /* Handle buffered writes. */
64 static char buffer[DEV_BSIZE];
65 static size_t fragsz;
66 
67 int dumpsys_minidump = 1;
68 SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0,
69     "Kernel makes compressed crash dumps");
70 
71 static int
72 buf_write(struct dumperinfo *di, char *ptr, size_t sz)
73 {
74 	size_t len;
75 	int error;
76 
77 	while (sz) {
78 		len = DEV_BSIZE - fragsz;
79 		if (len > sz)
80 			len = sz;
81 		bcopy(ptr, buffer + fragsz, len);
82 		fragsz += len;
83 		ptr += len;
84 		sz -= len;
85 		if (fragsz == DEV_BSIZE) {
86 			error = di->dumper(di->priv, buffer, 0, dumplo,
87 			    DEV_BSIZE);
88 			if (error)
89 				return error;
90 			dumplo += DEV_BSIZE;
91 			fragsz = 0;
92 		}
93 	}
94 
95 	return (0);
96 }
97 
98 static int
99 buf_flush(struct dumperinfo *di)
100 {
101 	int error;
102 
103 	if (fragsz == 0)
104 		return (0);
105 
106 	error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE);
107 	dumplo += DEV_BSIZE;
108 	fragsz = 0;
109 	return (error);
110 }
111 
112 static int
113 cb_dumpdata(struct pmap_md *md, int seqnr, void *arg)
114 {
115 	struct dumperinfo *di = (struct dumperinfo*)arg;
116 	vm_offset_t va;
117 	size_t counter, ofs, resid, sz;
118 	int c, error, twiddle;
119 
120 	error = 0;
121 	counter = 0;	/* Update twiddle every 16MB */
122 	twiddle = 0;
123 
124 	ofs = 0;	/* Logical offset within the chunk */
125 	resid = md->md_size;
126 
127 	printf("  chunk %d: %lu bytes ", seqnr, (u_long)resid);
128 
129 	while (resid) {
130 		sz = (resid > DFLTPHYS) ? DFLTPHYS : resid;
131 		va = pmap_dumpsys_map(md, ofs, &sz);
132 		counter += sz;
133 		if (counter >> 24) {
134 			printf("%c\b", "|/-\\"[twiddle++ & 3]);
135 			counter &= (1<<24) - 1;
136 		}
137 #ifdef SW_WATCHDOG
138 		wdog_kern_pat(WD_LASTVAL);
139 #endif
140 		error = di->dumper(di->priv, (void*)va, 0, dumplo, sz);
141 		pmap_dumpsys_unmap(md, ofs, va);
142 		if (error)
143 			break;
144 		dumplo += sz;
145 		resid -= sz;
146 		ofs += sz;
147 
148 		/* Check for user abort. */
149 		c = cncheckc();
150 		if (c == 0x03)
151 			return (ECANCELED);
152 		if (c != -1)
153 			printf("(CTRL-C to abort)  ");
154 	}
155 	printf("... %s\n", (error) ? "fail" : "ok");
156 	return (error);
157 }
158 
159 static int
160 cb_dumphdr(struct pmap_md *md, int seqnr, void *arg)
161 {
162 	struct dumperinfo *di = (struct dumperinfo*)arg;
163 	Elf32_Phdr phdr;
164 	int error;
165 
166 	bzero(&phdr, sizeof(phdr));
167 	phdr.p_type = PT_LOAD;
168 	phdr.p_flags = PF_R;			/* XXX */
169 	phdr.p_offset = fileofs;
170 	phdr.p_vaddr = md->md_vaddr;
171 	phdr.p_paddr = md->md_paddr;
172 	phdr.p_filesz = md->md_size;
173 	phdr.p_memsz = md->md_size;
174 	phdr.p_align = PAGE_SIZE;
175 
176 	error = buf_write(di, (char*)&phdr, sizeof(phdr));
177 	fileofs += phdr.p_filesz;
178 	return (error);
179 }
180 
181 static int
182 cb_size(struct pmap_md *md, int seqnr, void *arg)
183 {
184 	uint32_t *sz = (uint32_t*)arg;
185 
186 	*sz += md->md_size;
187 	return (0);
188 }
189 
190 static int
191 foreach_chunk(callback_t cb, void *arg)
192 {
193 	struct pmap_md *md;
194 	int error, seqnr;
195 
196 	seqnr = 0;
197 	md = pmap_scan_md(NULL);
198 	while (md != NULL) {
199 		error = (*cb)(md, seqnr++, arg);
200 		if (error)
201 			return (-error);
202 		md = pmap_scan_md(md);
203 	}
204 	return (seqnr);
205 }
206 
207 void
208 dumpsys(struct dumperinfo *di)
209 {
210 	Elf32_Ehdr ehdr;
211 	uint32_t dumpsize;
212 	off_t hdrgap;
213 	size_t hdrsz;
214 	int error;
215 
216 	bzero(&ehdr, sizeof(ehdr));
217 	ehdr.e_ident[EI_MAG0] = ELFMAG0;
218 	ehdr.e_ident[EI_MAG1] = ELFMAG1;
219 	ehdr.e_ident[EI_MAG2] = ELFMAG2;
220 	ehdr.e_ident[EI_MAG3] = ELFMAG3;
221 	ehdr.e_ident[EI_CLASS] = ELFCLASS32;
222 #if BYTE_ORDER == LITTLE_ENDIAN
223 	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
224 #else
225 	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
226 #endif
227 	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
228 	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
229 	ehdr.e_type = ET_CORE;
230 	ehdr.e_machine = EM_PPC;
231 	ehdr.e_phoff = sizeof(ehdr);
232 	ehdr.e_ehsize = sizeof(ehdr);
233 	ehdr.e_phentsize = sizeof(Elf32_Phdr);
234 	ehdr.e_shentsize = sizeof(Elf32_Shdr);
235 
236 	/* Calculate dump size. */
237 	dumpsize = 0L;
238 	ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize);
239 	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
240 	fileofs = MD_ALIGN(hdrsz);
241 	dumpsize += fileofs;
242 	hdrgap = fileofs - DEV_ALIGN(hdrsz);
243 
244 	/* For block devices, determine the dump offset on the device. */
245 	if (di->mediasize > 0) {
246 		if (di->mediasize <
247 		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
248 			error = ENOSPC;
249 			goto fail;
250 		}
251 		dumplo = di->mediaoffset + di->mediasize - dumpsize;
252 		dumplo -= sizeof(kdh) * 2;
253 	} else
254 		dumplo = 0;
255 
256 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize,
257 	    di->blocksize);
258 
259 	printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20,
260 	    ehdr.e_phnum);
261 
262 	/* Dump leader */
263 	error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
264 	if (error)
265 		goto fail;
266 	dumplo += sizeof(kdh);
267 
268 	/* Dump ELF header */
269 	error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
270 	if (error)
271 		goto fail;
272 
273 	/* Dump program headers */
274 	error = foreach_chunk(cb_dumphdr, di);
275 	if (error < 0)
276 		goto fail;
277 	buf_flush(di);
278 
279 	/*
280 	 * All headers are written using blocked I/O, so we know the
281 	 * current offset is (still) block aligned. Skip the alignement
282 	 * in the file to have the segment contents aligned at page
283 	 * boundary. We cannot use MD_ALIGN on dumplo, because we don't
284 	 * care and may very well be unaligned within the dump device.
285 	 */
286 	dumplo += hdrgap;
287 
288 	/* Dump memory chunks (updates dumplo) */
289 	error = foreach_chunk(cb_dumpdata, di);
290 	if (error < 0)
291 		goto fail;
292 
293 	/* Dump trailer */
294 	error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
295 	if (error)
296 		goto fail;
297 
298 	/* Signal completion, signoff and exit stage left. */
299 	di->dumper(di->priv, NULL, 0, 0, 0);
300 	printf("\nDump complete\n");
301 	return;
302 
303  fail:
304 	if (error < 0)
305 		error = -error;
306 
307 	if (error == ECANCELED)
308 		printf("\nDump aborted\n");
309 	else
310 		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
311 }
312