xref: /freebsd/sys/powerpc/powerpc/dump_machdep.c (revision 7661de35d15f582ab33e3bd6b8d909601557e436)
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, maxsz;
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 	maxsz = min(DFLTPHYS, di->maxiosize);
127 
128 	printf("  chunk %d: %lu bytes ", seqnr, (u_long)resid);
129 
130 	while (resid) {
131 		sz = min(resid, maxsz);
132 		va = pmap_dumpsys_map(md, ofs, &sz);
133 		counter += sz;
134 		if (counter >> 24) {
135 			printf("%c\b", "|/-\\"[twiddle++ & 3]);
136 			counter &= (1<<24) - 1;
137 		}
138 #ifdef SW_WATCHDOG
139 		wdog_kern_pat(WD_LASTVAL);
140 #endif
141 		error = di->dumper(di->priv, (void*)va, 0, dumplo, sz);
142 		pmap_dumpsys_unmap(md, ofs, va);
143 		if (error)
144 			break;
145 		dumplo += sz;
146 		resid -= sz;
147 		ofs += sz;
148 
149 		/* Check for user abort. */
150 		c = cncheckc();
151 		if (c == 0x03)
152 			return (ECANCELED);
153 		if (c != -1)
154 			printf("(CTRL-C to abort)  ");
155 	}
156 	printf("... %s\n", (error) ? "fail" : "ok");
157 	return (error);
158 }
159 
160 static int
161 cb_dumphdr(struct pmap_md *md, int seqnr, void *arg)
162 {
163 	struct dumperinfo *di = (struct dumperinfo*)arg;
164 	Elf_Phdr phdr;
165 	int error;
166 
167 	bzero(&phdr, sizeof(phdr));
168 	phdr.p_type = PT_LOAD;
169 	phdr.p_flags = PF_R;			/* XXX */
170 	phdr.p_offset = fileofs;
171 	phdr.p_vaddr = md->md_vaddr;
172 	phdr.p_paddr = md->md_paddr;
173 	phdr.p_filesz = md->md_size;
174 	phdr.p_memsz = md->md_size;
175 	phdr.p_align = PAGE_SIZE;
176 
177 	error = buf_write(di, (char*)&phdr, sizeof(phdr));
178 	fileofs += phdr.p_filesz;
179 	return (error);
180 }
181 
182 static int
183 cb_size(struct pmap_md *md, int seqnr, void *arg)
184 {
185 	uint32_t *sz = (uint32_t*)arg;
186 
187 	*sz += md->md_size;
188 	return (0);
189 }
190 
191 static int
192 foreach_chunk(callback_t cb, void *arg)
193 {
194 	struct pmap_md *md;
195 	int error, seqnr;
196 
197 	seqnr = 0;
198 	md = pmap_scan_md(NULL);
199 	while (md != NULL) {
200 		error = (*cb)(md, seqnr++, arg);
201 		if (error)
202 			return (-error);
203 		md = pmap_scan_md(md);
204 	}
205 	return (seqnr);
206 }
207 
208 void
209 dumpsys(struct dumperinfo *di)
210 {
211 	Elf_Ehdr ehdr;
212 	uint32_t dumpsize;
213 	off_t hdrgap;
214 	size_t hdrsz;
215 	int error;
216 
217 	bzero(&ehdr, sizeof(ehdr));
218 	ehdr.e_ident[EI_MAG0] = ELFMAG0;
219 	ehdr.e_ident[EI_MAG1] = ELFMAG1;
220 	ehdr.e_ident[EI_MAG2] = ELFMAG2;
221 	ehdr.e_ident[EI_MAG3] = ELFMAG3;
222 	ehdr.e_ident[EI_CLASS] = ELF_TARG_CLASS;
223 #if BYTE_ORDER == LITTLE_ENDIAN
224 	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
225 #else
226 	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
227 #endif
228 	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
229 	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
230 	ehdr.e_type = ET_CORE;
231 	ehdr.e_machine = ELF_ARCH;      /* Defined in powerpc/include/elf.h */
232 	ehdr.e_phoff = sizeof(ehdr);
233 	ehdr.e_ehsize = sizeof(ehdr);
234 	ehdr.e_phentsize = sizeof(Elf_Phdr);
235 	ehdr.e_shentsize = sizeof(Elf_Shdr);
236 
237 	/* Calculate dump size. */
238 	dumpsize = 0L;
239 	ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize);
240 	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
241 	fileofs = MD_ALIGN(hdrsz);
242 	dumpsize += fileofs;
243 	hdrgap = fileofs - DEV_ALIGN(hdrsz);
244 
245 	/* For block devices, determine the dump offset on the device. */
246 	if (di->mediasize > 0) {
247 		if (di->mediasize <
248 		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
249 			error = ENOSPC;
250 			goto fail;
251 		}
252 		dumplo = di->mediaoffset + di->mediasize - dumpsize;
253 		dumplo -= sizeof(kdh) * 2;
254 	} else
255 		dumplo = 0;
256 
257 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize,
258 	    di->blocksize);
259 
260 	printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20,
261 	    ehdr.e_phnum);
262 
263 	/* Dump leader */
264 	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
265 	if (error)
266 		goto fail;
267 	dumplo += sizeof(kdh);
268 
269 	/* Dump ELF header */
270 	error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
271 	if (error)
272 		goto fail;
273 
274 	/* Dump program headers */
275 	error = foreach_chunk(cb_dumphdr, di);
276 	if (error < 0)
277 		goto fail;
278 	buf_flush(di);
279 
280 	/*
281 	 * All headers are written using blocked I/O, so we know the
282 	 * current offset is (still) block aligned. Skip the alignement
283 	 * in the file to have the segment contents aligned at page
284 	 * boundary. We cannot use MD_ALIGN on dumplo, because we don't
285 	 * care and may very well be unaligned within the dump device.
286 	 */
287 	dumplo += hdrgap;
288 
289 	/* Dump memory chunks (updates dumplo) */
290 	error = foreach_chunk(cb_dumpdata, di);
291 	if (error < 0)
292 		goto fail;
293 
294 	/* Dump trailer */
295 	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
296 	if (error)
297 		goto fail;
298 
299 	/* Signal completion, signoff and exit stage left. */
300 	dump_write(di, NULL, 0, 0, 0);
301 	printf("\nDump complete\n");
302 	return;
303 
304  fail:
305 	if (error < 0)
306 		error = -error;
307 
308 	if (error == ECANCELED)
309 		printf("\nDump aborted\n");
310 	else if (error == ENOSPC)
311 		printf("\nDump failed. Partition too small.\n");
312 	else
313 		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
314 }
315