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