kern_dump.c revision 298310
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/kern/kern_dump.c 298310 2016-04-19 23:48:27Z pfg $");
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/proc.h>
38#include <sys/kerneldump.h>
39#ifdef SW_WATCHDOG
40#include <sys/watchdog.h>
41#endif
42#include <vm/vm.h>
43#include <vm/vm_param.h>
44#include <vm/pmap.h>
45#include <machine/dump.h>
46#include <machine/elf.h>
47#include <machine/md_var.h>
48#include <machine/pcb.h>
49
50CTASSERT(sizeof(struct kerneldumpheader) == 512);
51
52/*
53 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
54 * is to protect us from metadata and to protect metadata from us.
55 */
56#define	SIZEOF_METADATA		(64*1024)
57
58#define	MD_ALIGN(x)	roundup2((off_t)(x), PAGE_SIZE)
59
60off_t dumplo;
61
62/* Handle buffered writes. */
63static size_t fragsz;
64
65struct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS];
66
67#if !defined(__powerpc__) && !defined(__sparc__)
68void
69dumpsys_gen_pa_init(void)
70{
71	int n, idx;
72
73	bzero(dump_map, sizeof(dump_map));
74	for (n = 0; n < nitems(dump_map); n++) {
75		idx = n * 2;
76		if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0)
77			break;
78		dump_map[n].pa_start = dump_avail[idx];
79		dump_map[n].pa_size = dump_avail[idx + 1] - dump_avail[idx];
80	}
81}
82#endif
83
84struct dump_pa *
85dumpsys_gen_pa_next(struct dump_pa *mdp)
86{
87
88	if (mdp == NULL)
89		return (&dump_map[0]);
90
91	mdp++;
92	if (mdp->pa_size == 0)
93		mdp = NULL;
94	return (mdp);
95}
96
97void
98dumpsys_gen_wbinv_all(void)
99{
100
101}
102
103void
104dumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused,
105    void *va __unused)
106{
107
108}
109
110#if !defined(__sparc__)
111int
112dumpsys_gen_write_aux_headers(struct dumperinfo *di)
113{
114
115	return (0);
116}
117#endif
118
119int
120dumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz)
121{
122	size_t len;
123	int error;
124
125	while (sz) {
126		len = di->blocksize - fragsz;
127		if (len > sz)
128			len = sz;
129		memcpy((char *)di->blockbuf + fragsz, ptr, len);
130		fragsz += len;
131		ptr += len;
132		sz -= len;
133		if (fragsz == di->blocksize) {
134			error = dump_write(di, di->blockbuf, 0, dumplo,
135			    di->blocksize);
136			if (error)
137				return (error);
138			dumplo += di->blocksize;
139			fragsz = 0;
140		}
141	}
142	return (0);
143}
144
145int
146dumpsys_buf_flush(struct dumperinfo *di)
147{
148	int error;
149
150	if (fragsz == 0)
151		return (0);
152
153	error = dump_write(di, di->blockbuf, 0, dumplo, di->blocksize);
154	dumplo += di->blocksize;
155	fragsz = 0;
156	return (error);
157}
158
159CTASSERT(PAGE_SHIFT < 20);
160#define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT))
161
162int
163dumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg)
164{
165	struct dumperinfo *di = (struct dumperinfo*)arg;
166	vm_paddr_t pa;
167	void *va;
168	uint64_t pgs;
169	size_t counter, sz, chunk;
170	int c, error;
171	u_int maxdumppgs;
172
173	error = 0;	/* catch case in which chunk size is 0 */
174	counter = 0;	/* Update twiddle every 16MB */
175	va = NULL;
176	pgs = mdp->pa_size / PAGE_SIZE;
177	pa = mdp->pa_start;
178	maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS);
179	if (maxdumppgs == 0)	/* seatbelt */
180		maxdumppgs = 1;
181
182	printf("  chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs),
183	    (uintmax_t)pgs);
184
185	dumpsys_wbinv_all();
186	while (pgs) {
187		chunk = pgs;
188		if (chunk > maxdumppgs)
189			chunk = maxdumppgs;
190		sz = chunk << PAGE_SHIFT;
191		counter += sz;
192		if (counter >> 24) {
193			printf(" %ju", (uintmax_t)PG2MB(pgs));
194			counter &= (1 << 24) - 1;
195		}
196
197		dumpsys_map_chunk(pa, chunk, &va);
198#ifdef SW_WATCHDOG
199		wdog_kern_pat(WD_LASTVAL);
200#endif
201
202		error = dump_write(di, va, 0, dumplo, sz);
203		dumpsys_unmap_chunk(pa, chunk, va);
204		if (error)
205			break;
206		dumplo += sz;
207		pgs -= chunk;
208		pa += sz;
209
210		/* Check for user abort. */
211		c = cncheckc();
212		if (c == 0x03)
213			return (ECANCELED);
214		if (c != -1)
215			printf(" (CTRL-C to abort) ");
216	}
217	printf(" ... %s\n", (error) ? "fail" : "ok");
218	return (error);
219}
220
221int
222dumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg)
223{
224	struct dump_pa *mdp;
225	int error, seqnr;
226
227	seqnr = 0;
228	mdp = dumpsys_pa_next(NULL);
229	while (mdp != NULL) {
230		error = (*cb)(mdp, seqnr++, arg);
231		if (error)
232			return (-error);
233		mdp = dumpsys_pa_next(mdp);
234	}
235	return (seqnr);
236}
237
238#if !defined(__sparc__)
239static off_t fileofs;
240
241static int
242cb_dumphdr(struct dump_pa *mdp, int seqnr, void *arg)
243{
244	struct dumperinfo *di = (struct dumperinfo*)arg;
245	Elf_Phdr phdr;
246	uint64_t size;
247	int error;
248
249	size = mdp->pa_size;
250	bzero(&phdr, sizeof(phdr));
251	phdr.p_type = PT_LOAD;
252	phdr.p_flags = PF_R;			/* XXX */
253	phdr.p_offset = fileofs;
254#ifdef __powerpc__
255	phdr.p_vaddr = (do_minidump? mdp->pa_start : ~0L);
256	phdr.p_paddr = (do_minidump? ~0L : mdp->pa_start);
257#else
258	phdr.p_vaddr = mdp->pa_start;
259	phdr.p_paddr = mdp->pa_start;
260#endif
261	phdr.p_filesz = size;
262	phdr.p_memsz = size;
263	phdr.p_align = PAGE_SIZE;
264
265	error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr));
266	fileofs += phdr.p_filesz;
267	return (error);
268}
269
270static int
271cb_size(struct dump_pa *mdp, int seqnr, void *arg)
272{
273	uint64_t *sz;
274
275	sz = (uint64_t *)arg;
276	*sz += (uint64_t)mdp->pa_size;
277	return (0);
278}
279
280int
281dumpsys_generic(struct dumperinfo *di)
282{
283	static struct kerneldumpheader kdh;
284	Elf_Ehdr ehdr;
285	uint64_t dumpsize;
286	off_t hdrgap;
287	size_t hdrsz, size;
288	int error;
289
290#ifndef __powerpc__
291	if (do_minidump)
292		return (minidumpsys(di));
293#endif
294
295	bzero(&ehdr, sizeof(ehdr));
296	ehdr.e_ident[EI_MAG0] = ELFMAG0;
297	ehdr.e_ident[EI_MAG1] = ELFMAG1;
298	ehdr.e_ident[EI_MAG2] = ELFMAG2;
299	ehdr.e_ident[EI_MAG3] = ELFMAG3;
300	ehdr.e_ident[EI_CLASS] = ELF_CLASS;
301#if BYTE_ORDER == LITTLE_ENDIAN
302	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
303#else
304	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
305#endif
306	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
307	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
308	ehdr.e_type = ET_CORE;
309	ehdr.e_machine = EM_VALUE;
310	ehdr.e_phoff = sizeof(ehdr);
311	ehdr.e_flags = 0;
312	ehdr.e_ehsize = sizeof(ehdr);
313	ehdr.e_phentsize = sizeof(Elf_Phdr);
314	ehdr.e_shentsize = sizeof(Elf_Shdr);
315
316	dumpsys_pa_init();
317
318	/* Calculate dump size. */
319	dumpsize = 0L;
320	ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize) +
321	    DUMPSYS_NUM_AUX_HDRS;
322	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
323	fileofs = MD_ALIGN(hdrsz);
324	dumpsize += fileofs;
325	hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize);
326
327	/* Determine dump offset on device. */
328	if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) {
329		error = ENOSPC;
330		goto fail;
331	}
332	dumplo = di->mediaoffset + di->mediasize - dumpsize;
333	dumplo -= di->blocksize * 2;
334
335	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize,
336	    di->blocksize);
337
338	printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20,
339	    ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS);
340
341	/* Dump leader */
342	error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size);
343	if (error)
344		goto fail;
345	dumplo += size;
346
347	/* Dump ELF header */
348	error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr));
349	if (error)
350		goto fail;
351
352	/* Dump program headers */
353	error = dumpsys_foreach_chunk(cb_dumphdr, di);
354	if (error < 0)
355		goto fail;
356	error = dumpsys_write_aux_headers(di);
357	if (error < 0)
358		goto fail;
359	dumpsys_buf_flush(di);
360
361	/*
362	 * All headers are written using blocked I/O, so we know the
363	 * current offset is (still) block aligned. Skip the alignement
364	 * in the file to have the segment contents aligned at page
365	 * boundary. We cannot use MD_ALIGN on dumplo, because we don't
366	 * care and may very well be unaligned within the dump device.
367	 */
368	dumplo += hdrgap;
369
370	/* Dump memory chunks (updates dumplo) */
371	error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di);
372	if (error < 0)
373		goto fail;
374
375	/* Dump trailer */
376	error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size);
377	if (error)
378		goto fail;
379
380	/* Signal completion, signoff and exit stage left. */
381	dump_write(di, NULL, 0, 0, 0);
382	printf("\nDump complete\n");
383	return (0);
384
385 fail:
386	if (error < 0)
387		error = -error;
388
389	if (error == ECANCELED)
390		printf("\nDump aborted\n");
391	else if (error == ENOSPC)
392		printf("\nDump failed. Partition too small.\n");
393	else
394		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
395	return (error);
396}
397#endif
398