dump_machdep.c revision 190684
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: head/sys/powerpc/powerpc/dump_machdep.c 190684 2009-04-04 02:12:37Z marcel $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/conf.h>
33#include <sys/cons.h>
34#include <sys/kernel.h>
35#include <sys/kerneldump.h>
36#include <sys/sysctl.h>
37#include <vm/vm.h>
38#include <vm/pmap.h>
39#include <machine/elf.h>
40#include <machine/md_var.h>
41
42CTASSERT(sizeof(struct kerneldumpheader) == 512);
43
44/*
45 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
46 * is to protect us from metadata and to protect metadata from us.
47 */
48#define	SIZEOF_METADATA		(64*1024)
49
50#define	MD_ALIGN(x)	(((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
51#define	DEV_ALIGN(x)	(((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
52
53typedef int callback_t(struct pmap_md *, int, void *);
54
55static struct kerneldumpheader kdh;
56static off_t dumplo, fileofs;
57
58/* Handle buffered writes. */
59static char buffer[DEV_BSIZE];
60static size_t fragsz;
61
62int dumpsys_minidump = 1;
63SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0,
64    "Kernel makes compressed crash dumps");
65
66static int
67buf_write(struct dumperinfo *di, char *ptr, size_t sz)
68{
69	size_t len;
70	int error;
71
72	while (sz) {
73		len = DEV_BSIZE - fragsz;
74		if (len > sz)
75			len = sz;
76		bcopy(ptr, buffer + fragsz, len);
77		fragsz += len;
78		ptr += len;
79		sz -= len;
80		if (fragsz == DEV_BSIZE) {
81			error = di->dumper(di->priv, buffer, 0, dumplo,
82			    DEV_BSIZE);
83			if (error)
84				return error;
85			dumplo += DEV_BSIZE;
86			fragsz = 0;
87		}
88	}
89
90	return (0);
91}
92
93static int
94buf_flush(struct dumperinfo *di)
95{
96	int error;
97
98	if (fragsz == 0)
99		return (0);
100
101	error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE);
102	dumplo += DEV_BSIZE;
103	fragsz = 0;
104	return (error);
105}
106
107static int
108cb_dumpdata(struct pmap_md *md, int seqnr, void *arg)
109{
110	struct dumperinfo *di = (struct dumperinfo*)arg;
111	vm_offset_t va;
112	size_t counter, ofs, resid, sz;
113	int c, error, twiddle;
114
115	error = 0;
116	counter = 0;	/* Update twiddle every 16MB */
117	twiddle = 0;
118
119	ofs = 0;	/* Logical offset within the chunk */
120	resid = md->md_size;
121
122	printf("  chunk %d: %lu bytes ", seqnr, (u_long)resid);
123
124	while (resid) {
125		sz = (resid > DFLTPHYS) ? DFLTPHYS : resid;
126		va = pmap_dumpsys_map(md, ofs, &sz);
127		counter += sz;
128		if (counter >> 24) {
129			printf("%c\b", "|/-\\"[twiddle++ & 3]);
130			counter &= (1<<24) - 1;
131		}
132		error = di->dumper(di->priv, (void*)va, 0, dumplo, sz);
133		pmap_dumpsys_unmap(md, ofs, va);
134		if (error)
135			break;
136		dumplo += sz;
137		resid -= sz;
138		ofs += sz;
139
140		/* Check for user abort. */
141		c = cncheckc();
142		if (c == 0x03)
143			return (ECANCELED);
144		if (c != -1)
145			printf("(CTRL-C to abort)  ");
146	}
147	printf("... %s\n", (error) ? "fail" : "ok");
148	return (error);
149}
150
151static int
152cb_dumphdr(struct pmap_md *md, int seqnr, void *arg)
153{
154	struct dumperinfo *di = (struct dumperinfo*)arg;
155	Elf32_Phdr phdr;
156	int error;
157
158	bzero(&phdr, sizeof(phdr));
159	phdr.p_type = PT_LOAD;
160	phdr.p_flags = PF_R;			/* XXX */
161	phdr.p_offset = fileofs;
162	phdr.p_vaddr = md->md_vaddr;
163	phdr.p_paddr = md->md_paddr;
164	phdr.p_filesz = md->md_size;
165	phdr.p_memsz = md->md_size;
166	phdr.p_align = PAGE_SIZE;
167
168	error = buf_write(di, (char*)&phdr, sizeof(phdr));
169	fileofs += phdr.p_filesz;
170	return (error);
171}
172
173static int
174cb_size(struct pmap_md *md, int seqnr, void *arg)
175{
176	uint32_t *sz = (uint32_t*)arg;
177
178	*sz += md->md_size;
179	return (0);
180}
181
182static int
183foreach_chunk(callback_t cb, void *arg)
184{
185	struct pmap_md *md;
186	int error, seqnr;
187
188	seqnr = 0;
189	md = pmap_scan_md(NULL);
190	while (md != NULL) {
191		error = (*cb)(md, seqnr++, arg);
192		if (error)
193			return (-error);
194		md = pmap_scan_md(md);
195	}
196	return (seqnr);
197}
198
199void
200dumpsys(struct dumperinfo *di)
201{
202	Elf32_Ehdr ehdr;
203	uint32_t dumpsize;
204	off_t hdrgap;
205	size_t hdrsz;
206	int error;
207
208	bzero(&ehdr, sizeof(ehdr));
209	ehdr.e_ident[EI_MAG0] = ELFMAG0;
210	ehdr.e_ident[EI_MAG1] = ELFMAG1;
211	ehdr.e_ident[EI_MAG2] = ELFMAG2;
212	ehdr.e_ident[EI_MAG3] = ELFMAG3;
213	ehdr.e_ident[EI_CLASS] = ELFCLASS32;
214#if BYTE_ORDER == LITTLE_ENDIAN
215	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
216#else
217	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
218#endif
219	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
220	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
221	ehdr.e_type = ET_CORE;
222	ehdr.e_machine = EM_PPC;
223	ehdr.e_phoff = sizeof(ehdr);
224	ehdr.e_ehsize = sizeof(ehdr);
225	ehdr.e_phentsize = sizeof(Elf32_Phdr);
226	ehdr.e_shentsize = sizeof(Elf32_Shdr);
227
228	/* Calculate dump size. */
229	dumpsize = 0L;
230	ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize);
231	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
232	fileofs = MD_ALIGN(hdrsz);
233	dumpsize += fileofs;
234	hdrgap = fileofs - DEV_ALIGN(hdrsz);
235
236	/* For block devices, determine the dump offset on the device. */
237	if (di->mediasize > 0) {
238		if (di->mediasize <
239		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
240			error = ENOSPC;
241			goto fail;
242		}
243		dumplo = di->mediaoffset + di->mediasize - dumpsize;
244		dumplo -= sizeof(kdh) * 2;
245	} else
246		dumplo = 0;
247
248	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize,
249	    di->blocksize);
250
251	printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20,
252	    ehdr.e_phnum);
253
254	/* Dump leader */
255	error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
256	if (error)
257		goto fail;
258	dumplo += sizeof(kdh);
259
260	/* Dump ELF header */
261	error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
262	if (error)
263		goto fail;
264
265	/* Dump program headers */
266	error = foreach_chunk(cb_dumphdr, di);
267	if (error < 0)
268		goto fail;
269	buf_flush(di);
270
271	/*
272	 * All headers are written using blocked I/O, so we know the
273	 * current offset is (still) block aligned. Skip the alignement
274	 * in the file to have the segment contents aligned at page
275	 * boundary. We cannot use MD_ALIGN on dumplo, because we don't
276	 * care and may very well be unaligned within the dump device.
277	 */
278	dumplo += hdrgap;
279
280	/* Dump memory chunks (updates dumplo) */
281	error = foreach_chunk(cb_dumpdata, di);
282	if (error < 0)
283		goto fail;
284
285	/* Dump trailer */
286	error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
287	if (error)
288		goto fail;
289
290	/* Signal completion, signoff and exit stage left. */
291	di->dumper(di->priv, NULL, 0, 0, 0);
292	printf("\nDump complete\n");
293	return;
294
295 fail:
296	if (error < 0)
297		error = -error;
298
299	if (error == ECANCELED)
300		printf("\nDump aborted\n");
301	else
302		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
303}
304