1190684Smarcel/*-
2190684Smarcel * Copyright (c) 2002 Marcel Moolenaar
3190684Smarcel * All rights reserved.
4190684Smarcel *
5190684Smarcel * Redistribution and use in source and binary forms, with or without
6190684Smarcel * modification, are permitted provided that the following conditions
7190684Smarcel * are met:
8190684Smarcel *
9190684Smarcel * 1. Redistributions of source code must retain the above copyright
10190684Smarcel *    notice, this list of conditions and the following disclaimer.
11190684Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12190684Smarcel *    notice, this list of conditions and the following disclaimer in the
13190684Smarcel *    documentation and/or other materials provided with the distribution.
14190684Smarcel *
15190684Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16190684Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17190684Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18190684Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19190684Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20190684Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21190684Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22190684Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23190684Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24190684Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25190684Smarcel */
26190684Smarcel
27190684Smarcel#include <sys/cdefs.h>
28190684Smarcel__FBSDID("$FreeBSD$");
29190684Smarcel
30221173Sattilio#include "opt_watchdog.h"
31221173Sattilio
32190684Smarcel#include <sys/param.h>
33190684Smarcel#include <sys/systm.h>
34190684Smarcel#include <sys/conf.h>
35190684Smarcel#include <sys/cons.h>
36190684Smarcel#include <sys/kernel.h>
37190684Smarcel#include <sys/kerneldump.h>
38190684Smarcel#include <sys/sysctl.h>
39221173Sattilio#ifdef SW_WATCHDOG
40221173Sattilio#include <sys/watchdog.h>
41221173Sattilio#endif
42190684Smarcel#include <vm/vm.h>
43190684Smarcel#include <vm/pmap.h>
44190684Smarcel#include <machine/elf.h>
45190684Smarcel#include <machine/md_var.h>
46190684Smarcel
47190684SmarcelCTASSERT(sizeof(struct kerneldumpheader) == 512);
48190684Smarcel
49190684Smarcel/*
50190684Smarcel * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
51190684Smarcel * is to protect us from metadata and to protect metadata from us.
52190684Smarcel */
53190684Smarcel#define	SIZEOF_METADATA		(64*1024)
54190684Smarcel
55190684Smarcel#define	MD_ALIGN(x)	(((off_t)(x) + PAGE_MASK) & ~PAGE_MASK)
56190684Smarcel#define	DEV_ALIGN(x)	(((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
57190684Smarcel
58190684Smarceltypedef int callback_t(struct pmap_md *, int, void *);
59190684Smarcel
60190684Smarcelstatic struct kerneldumpheader kdh;
61190684Smarcelstatic off_t dumplo, fileofs;
62190684Smarcel
63190684Smarcel/* Handle buffered writes. */
64190684Smarcelstatic char buffer[DEV_BSIZE];
65190684Smarcelstatic size_t fragsz;
66190684Smarcel
67190684Smarcelint dumpsys_minidump = 1;
68190684SmarcelSYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RD, &dumpsys_minidump, 0,
69190684Smarcel    "Kernel makes compressed crash dumps");
70190684Smarcel
71190684Smarcelstatic int
72190684Smarcelbuf_write(struct dumperinfo *di, char *ptr, size_t sz)
73190684Smarcel{
74190684Smarcel	size_t len;
75190684Smarcel	int error;
76190684Smarcel
77190684Smarcel	while (sz) {
78190684Smarcel		len = DEV_BSIZE - fragsz;
79190684Smarcel		if (len > sz)
80190684Smarcel			len = sz;
81190684Smarcel		bcopy(ptr, buffer + fragsz, len);
82190684Smarcel		fragsz += len;
83190684Smarcel		ptr += len;
84190684Smarcel		sz -= len;
85190684Smarcel		if (fragsz == DEV_BSIZE) {
86190684Smarcel			error = di->dumper(di->priv, buffer, 0, dumplo,
87190684Smarcel			    DEV_BSIZE);
88190684Smarcel			if (error)
89190684Smarcel				return error;
90190684Smarcel			dumplo += DEV_BSIZE;
91190684Smarcel			fragsz = 0;
92190684Smarcel		}
93190684Smarcel	}
94190684Smarcel
95190684Smarcel	return (0);
96190684Smarcel}
97190684Smarcel
98190684Smarcelstatic int
99190684Smarcelbuf_flush(struct dumperinfo *di)
100190684Smarcel{
101190684Smarcel	int error;
102190684Smarcel
103190684Smarcel	if (fragsz == 0)
104190684Smarcel		return (0);
105190684Smarcel
106190684Smarcel	error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE);
107190684Smarcel	dumplo += DEV_BSIZE;
108190684Smarcel	fragsz = 0;
109190684Smarcel	return (error);
110190684Smarcel}
111190684Smarcel
112190684Smarcelstatic int
113190684Smarcelcb_dumpdata(struct pmap_md *md, int seqnr, void *arg)
114190684Smarcel{
115190684Smarcel	struct dumperinfo *di = (struct dumperinfo*)arg;
116190684Smarcel	vm_offset_t va;
117190684Smarcel	size_t counter, ofs, resid, sz;
118190684Smarcel	int c, error, twiddle;
119190684Smarcel
120190684Smarcel	error = 0;
121190684Smarcel	counter = 0;	/* Update twiddle every 16MB */
122190684Smarcel	twiddle = 0;
123190684Smarcel
124190684Smarcel	ofs = 0;	/* Logical offset within the chunk */
125190684Smarcel	resid = md->md_size;
126190684Smarcel
127190684Smarcel	printf("  chunk %d: %lu bytes ", seqnr, (u_long)resid);
128190684Smarcel
129190684Smarcel	while (resid) {
130190684Smarcel		sz = (resid > DFLTPHYS) ? DFLTPHYS : resid;
131190684Smarcel		va = pmap_dumpsys_map(md, ofs, &sz);
132190684Smarcel		counter += sz;
133190684Smarcel		if (counter >> 24) {
134190684Smarcel			printf("%c\b", "|/-\\"[twiddle++ & 3]);
135190684Smarcel			counter &= (1<<24) - 1;
136190684Smarcel		}
137221173Sattilio#ifdef SW_WATCHDOG
138221173Sattilio		wdog_kern_pat(WD_LASTVAL);
139221173Sattilio#endif
140190684Smarcel		error = di->dumper(di->priv, (void*)va, 0, dumplo, sz);
141190684Smarcel		pmap_dumpsys_unmap(md, ofs, va);
142190684Smarcel		if (error)
143190684Smarcel			break;
144190684Smarcel		dumplo += sz;
145190684Smarcel		resid -= sz;
146190684Smarcel		ofs += sz;
147190684Smarcel
148190684Smarcel		/* Check for user abort. */
149190684Smarcel		c = cncheckc();
150190684Smarcel		if (c == 0x03)
151190684Smarcel			return (ECANCELED);
152190684Smarcel		if (c != -1)
153190684Smarcel			printf("(CTRL-C to abort)  ");
154190684Smarcel	}
155190684Smarcel	printf("... %s\n", (error) ? "fail" : "ok");
156190684Smarcel	return (error);
157190684Smarcel}
158190684Smarcel
159190684Smarcelstatic int
160190684Smarcelcb_dumphdr(struct pmap_md *md, int seqnr, void *arg)
161190684Smarcel{
162190684Smarcel	struct dumperinfo *di = (struct dumperinfo*)arg;
163190684Smarcel	Elf32_Phdr phdr;
164190684Smarcel	int error;
165190684Smarcel
166190684Smarcel	bzero(&phdr, sizeof(phdr));
167190684Smarcel	phdr.p_type = PT_LOAD;
168190684Smarcel	phdr.p_flags = PF_R;			/* XXX */
169190684Smarcel	phdr.p_offset = fileofs;
170190684Smarcel	phdr.p_vaddr = md->md_vaddr;
171190684Smarcel	phdr.p_paddr = md->md_paddr;
172190684Smarcel	phdr.p_filesz = md->md_size;
173190684Smarcel	phdr.p_memsz = md->md_size;
174190684Smarcel	phdr.p_align = PAGE_SIZE;
175190684Smarcel
176190684Smarcel	error = buf_write(di, (char*)&phdr, sizeof(phdr));
177190684Smarcel	fileofs += phdr.p_filesz;
178190684Smarcel	return (error);
179190684Smarcel}
180190684Smarcel
181190684Smarcelstatic int
182190684Smarcelcb_size(struct pmap_md *md, int seqnr, void *arg)
183190684Smarcel{
184190684Smarcel	uint32_t *sz = (uint32_t*)arg;
185190684Smarcel
186190684Smarcel	*sz += md->md_size;
187190684Smarcel	return (0);
188190684Smarcel}
189190684Smarcel
190190684Smarcelstatic int
191190684Smarcelforeach_chunk(callback_t cb, void *arg)
192190684Smarcel{
193190684Smarcel	struct pmap_md *md;
194190684Smarcel	int error, seqnr;
195190684Smarcel
196190684Smarcel	seqnr = 0;
197190684Smarcel	md = pmap_scan_md(NULL);
198190684Smarcel	while (md != NULL) {
199190684Smarcel		error = (*cb)(md, seqnr++, arg);
200190684Smarcel		if (error)
201190684Smarcel			return (-error);
202190684Smarcel		md = pmap_scan_md(md);
203190684Smarcel	}
204190684Smarcel	return (seqnr);
205190684Smarcel}
206190684Smarcel
207190684Smarcelvoid
208190684Smarceldumpsys(struct dumperinfo *di)
209190684Smarcel{
210190684Smarcel	Elf32_Ehdr ehdr;
211190684Smarcel	uint32_t dumpsize;
212190684Smarcel	off_t hdrgap;
213190684Smarcel	size_t hdrsz;
214190684Smarcel	int error;
215190684Smarcel
216190684Smarcel	bzero(&ehdr, sizeof(ehdr));
217190684Smarcel	ehdr.e_ident[EI_MAG0] = ELFMAG0;
218190684Smarcel	ehdr.e_ident[EI_MAG1] = ELFMAG1;
219190684Smarcel	ehdr.e_ident[EI_MAG2] = ELFMAG2;
220190684Smarcel	ehdr.e_ident[EI_MAG3] = ELFMAG3;
221190684Smarcel	ehdr.e_ident[EI_CLASS] = ELFCLASS32;
222190684Smarcel#if BYTE_ORDER == LITTLE_ENDIAN
223190684Smarcel	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
224190684Smarcel#else
225190684Smarcel	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
226190684Smarcel#endif
227190684Smarcel	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
228190684Smarcel	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
229190684Smarcel	ehdr.e_type = ET_CORE;
230190684Smarcel	ehdr.e_machine = EM_PPC;
231190684Smarcel	ehdr.e_phoff = sizeof(ehdr);
232190684Smarcel	ehdr.e_ehsize = sizeof(ehdr);
233190684Smarcel	ehdr.e_phentsize = sizeof(Elf32_Phdr);
234190684Smarcel	ehdr.e_shentsize = sizeof(Elf32_Shdr);
235190684Smarcel
236190684Smarcel	/* Calculate dump size. */
237190684Smarcel	dumpsize = 0L;
238190684Smarcel	ehdr.e_phnum = foreach_chunk(cb_size, &dumpsize);
239190684Smarcel	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
240190684Smarcel	fileofs = MD_ALIGN(hdrsz);
241190684Smarcel	dumpsize += fileofs;
242190684Smarcel	hdrgap = fileofs - DEV_ALIGN(hdrsz);
243190684Smarcel
244190684Smarcel	/* For block devices, determine the dump offset on the device. */
245190684Smarcel	if (di->mediasize > 0) {
246190684Smarcel		if (di->mediasize <
247190684Smarcel		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
248190684Smarcel			error = ENOSPC;
249190684Smarcel			goto fail;
250190684Smarcel		}
251190684Smarcel		dumplo = di->mediaoffset + di->mediasize - dumpsize;
252190684Smarcel		dumplo -= sizeof(kdh) * 2;
253190684Smarcel	} else
254190684Smarcel		dumplo = 0;
255190684Smarcel
256190684Smarcel	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize,
257190684Smarcel	    di->blocksize);
258190684Smarcel
259190684Smarcel	printf("Dumping %u MB (%d chunks)\n", dumpsize >> 20,
260190684Smarcel	    ehdr.e_phnum);
261190684Smarcel
262190684Smarcel	/* Dump leader */
263190684Smarcel	error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
264190684Smarcel	if (error)
265190684Smarcel		goto fail;
266190684Smarcel	dumplo += sizeof(kdh);
267190684Smarcel
268190684Smarcel	/* Dump ELF header */
269190684Smarcel	error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
270190684Smarcel	if (error)
271190684Smarcel		goto fail;
272190684Smarcel
273190684Smarcel	/* Dump program headers */
274190684Smarcel	error = foreach_chunk(cb_dumphdr, di);
275190684Smarcel	if (error < 0)
276190684Smarcel		goto fail;
277190684Smarcel	buf_flush(di);
278190684Smarcel
279190684Smarcel	/*
280190684Smarcel	 * All headers are written using blocked I/O, so we know the
281190684Smarcel	 * current offset is (still) block aligned. Skip the alignement
282190684Smarcel	 * in the file to have the segment contents aligned at page
283190684Smarcel	 * boundary. We cannot use MD_ALIGN on dumplo, because we don't
284190684Smarcel	 * care and may very well be unaligned within the dump device.
285190684Smarcel	 */
286190684Smarcel	dumplo += hdrgap;
287190684Smarcel
288190684Smarcel	/* Dump memory chunks (updates dumplo) */
289190684Smarcel	error = foreach_chunk(cb_dumpdata, di);
290190684Smarcel	if (error < 0)
291190684Smarcel		goto fail;
292190684Smarcel
293190684Smarcel	/* Dump trailer */
294190684Smarcel	error = di->dumper(di->priv, &kdh, 0, dumplo, sizeof(kdh));
295190684Smarcel	if (error)
296190684Smarcel		goto fail;
297190684Smarcel
298190684Smarcel	/* Signal completion, signoff and exit stage left. */
299190684Smarcel	di->dumper(di->priv, NULL, 0, 0, 0);
300190684Smarcel	printf("\nDump complete\n");
301190684Smarcel	return;
302190684Smarcel
303190684Smarcel fail:
304190684Smarcel	if (error < 0)
305190684Smarcel		error = -error;
306190684Smarcel
307190684Smarcel	if (error == ECANCELED)
308190684Smarcel		printf("\nDump aborted\n");
309190684Smarcel	else
310190684Smarcel		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
311190684Smarcel}
312