dump_machdep.c revision 270296
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/10/sys/ia64/ia64/dump_machdep.c 270296 2014-08-21 19:51:07Z emaste $");
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/efi.h>
37#include <sys/kernel.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/pmap.h>
44#include <machine/bootinfo.h>
45#include <machine/elf.h>
46#include <machine/md_var.h>
47
48CTASSERT(sizeof(struct kerneldumpheader) == 512);
49
50/*
51 * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
52 * is to protect us from metadata and to protect metadata from us.
53 */
54#define	SIZEOF_METADATA		(64*1024)
55
56#define	MD_ALIGN(x)	(((off_t)(x) + EFI_PAGE_MASK) & ~EFI_PAGE_MASK)
57#define	DEV_ALIGN(x)	(((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
58
59static int minidump = 0;
60TUNABLE_INT("debug.minidump", &minidump);
61SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &minidump, 0,
62    "Enable mini crash dumps");
63
64static struct kerneldumpheader kdh;
65static off_t dumplo, fileofs;
66
67/* Handle buffered writes. */
68static char buffer[DEV_BSIZE];
69static size_t fragsz;
70
71static int
72buf_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 = dump_write(di, 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
98static int
99buf_flush(struct dumperinfo *di)
100{
101	int error;
102
103	if (fragsz == 0)
104		return (0);
105
106	error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE);
107	dumplo += DEV_BSIZE;
108	fragsz = 0;
109	return (error);
110}
111
112/*
113 * Physical dump support
114 */
115
116typedef int phys_callback_t(struct efi_md*, int, void*);
117
118static int
119phys_cb_dumpdata(struct efi_md *mdp, int seqnr, void *arg)
120{
121	struct dumperinfo *di = (struct dumperinfo*)arg;
122	vm_offset_t pa;
123	uint64_t pgs;
124	size_t counter, sz;
125	int c, error, twiddle;
126
127	error = 0;	/* catch case in which mdp->md_pages is 0 */
128	counter = 0;	/* Update twiddle every 16MB */
129	twiddle = 0;
130	pgs = mdp->md_pages;
131	pa = IA64_PHYS_TO_RR7(mdp->md_phys);
132
133	printf("  chunk %d: %ld pages ", seqnr, (long)pgs);
134
135	while (pgs) {
136		sz = (pgs > (DFLTPHYS >> EFI_PAGE_SHIFT))
137		    ? DFLTPHYS : pgs << EFI_PAGE_SHIFT;
138		counter += sz;
139		if (counter >> 24) {
140			printf("%c\b", "|/-\\"[twiddle++ & 3]);
141			counter &= (1<<24) - 1;
142		}
143#ifdef SW_WATCHDOG
144		wdog_kern_pat(WD_LASTVAL);
145#endif
146		error = dump_write(di, (void*)pa, 0, dumplo, sz);
147		if (error)
148			break;
149		dumplo += sz;
150		pgs -= sz >> EFI_PAGE_SHIFT;
151		pa += sz;
152
153		/* Check for user abort. */
154		c = cncheckc();
155		if (c == 0x03)
156			return (ECANCELED);
157		if (c != -1)
158			printf("(CTRL-C to abort)  ");
159	}
160	printf("... %s\n", (error) ? "fail" : "ok");
161	return (error);
162}
163
164static int
165phys_cb_dumphdr(struct efi_md *mdp, int seqnr, void *arg)
166{
167	struct dumperinfo *di = (struct dumperinfo*)arg;
168	Elf64_Phdr phdr;
169	int error;
170
171	bzero(&phdr, sizeof(phdr));
172	phdr.p_type = PT_LOAD;
173	phdr.p_flags = PF_R;			/* XXX */
174	phdr.p_offset = fileofs;
175	phdr.p_vaddr = (uintptr_t)mdp->md_virt;	/* XXX probably bogus. */
176	phdr.p_paddr = mdp->md_phys;
177	phdr.p_filesz = mdp->md_pages << EFI_PAGE_SHIFT;
178	phdr.p_memsz = mdp->md_pages << EFI_PAGE_SHIFT;
179	phdr.p_align = EFI_PAGE_SIZE;
180
181	error = buf_write(di, (char*)&phdr, sizeof(phdr));
182	fileofs += phdr.p_filesz;
183	return (error);
184}
185
186static int
187phys_cb_size(struct efi_md *mdp, int seqnr, void *arg)
188{
189	uint64_t *sz = (uint64_t*)arg;
190
191	*sz += (uint64_t)mdp->md_pages << EFI_PAGE_SHIFT;
192	return (0);
193}
194
195static int
196phys_foreach(phys_callback_t cb, void *arg)
197{
198	struct efi_md *mdp;
199	int error, seqnr;
200
201	seqnr = 0;
202	mdp = efi_md_first();
203	while (mdp != NULL) {
204		if (mdp->md_type == EFI_MD_TYPE_FREE ||
205		    mdp->md_type == EFI_MD_TYPE_DATA ||
206		    mdp->md_type == EFI_MD_TYPE_CODE ||
207		    mdp->md_type == EFI_MD_TYPE_BS_DATA ||
208		    mdp->md_type == EFI_MD_TYPE_BS_CODE) {
209			error = (*cb)(mdp, seqnr++, arg);
210			if (error)
211				return (-error);
212		}
213		mdp = efi_md_next(mdp);
214	}
215	return (seqnr);
216}
217
218/*
219 * Virtual dump (aka minidump) support
220 */
221
222typedef int virt_callback_t(vm_offset_t, vm_size_t, int, void*);
223
224static int
225virt_cb_size(vm_offset_t va, vm_size_t sz, int seqnr, void *arg)
226{
227	uint64_t *dumpsize = (uint64_t *)arg;
228
229	*dumpsize += sz;
230	return (0);
231}
232
233static int
234virt_cb_dumphdr(vm_offset_t va, vm_size_t sz, int seqnr, void *arg)
235{
236	struct dumperinfo *di = (struct dumperinfo *)arg;
237	Elf64_Phdr phdr;
238	int error;
239
240	bzero(&phdr, sizeof(phdr));
241	phdr.p_type = PT_LOAD;
242	phdr.p_flags = PF_R;			/* XXX */
243	phdr.p_offset = fileofs;
244	phdr.p_vaddr = va;
245	phdr.p_paddr = ~0UL;
246	phdr.p_filesz = sz;
247	phdr.p_memsz = sz;
248	phdr.p_align = PAGE_SIZE;
249
250	error = buf_write(di, (char*)&phdr, sizeof(phdr));
251	fileofs += phdr.p_filesz;
252	return (error);
253}
254
255static int
256virt_cb_dumpdata(vm_offset_t va, vm_size_t sz, int seqnr, void *arg)
257{
258	struct dumperinfo *di = (struct dumperinfo *)arg;
259	size_t counter, iosz;
260	int c, error, twiddle;
261
262	error = 0;	/* catch case in which pgs is 0 */
263	counter = 0;	/* Update twiddle every 16MB */
264	twiddle = 0;
265
266	printf("  chunk %d: %ld pages ", seqnr, atop(sz));
267
268	while (sz) {
269		iosz = (sz > DFLTPHYS) ? DFLTPHYS : sz;
270		counter += iosz;
271		if (counter >> 24) {
272			printf("%c\b", "|/-\\"[twiddle++ & 3]);
273			counter &= (1<<24) - 1;
274		}
275#ifdef SW_WATCHDOG
276		wdog_kern_pat(WD_LASTVAL);
277#endif
278		error = dump_write(di, (void*)va, 0, dumplo, iosz);
279		if (error)
280			break;
281		dumplo += iosz;
282		sz -= iosz;
283		va += iosz;
284
285		/* Check for user abort. */
286		c = cncheckc();
287		if (c == 0x03)
288			return (ECANCELED);
289		if (c != -1)
290			printf("(CTRL-C to abort)  ");
291	}
292	printf("... %s\n", (error) ? "fail" : "ok");
293	return (error);
294}
295
296static int
297virt_foreach(virt_callback_t cb, void *arg)
298{
299	vm_offset_t va;
300	vm_size_t sz;
301	int error, seqnr;
302
303	seqnr = 0;
304	while (1) {
305		switch (seqnr) {
306		case 0:
307			va = IA64_PBVM_BASE;
308			sz = round_page(bootinfo->bi_kernend) - va;
309			break;
310		default:
311			va = 0;
312			sz = 0;
313			break;
314		}
315		if (va == 0 && sz == 0)
316			break;
317		error = (*cb)(va, sz, seqnr, arg);
318		if (error)
319			return (-error);
320		seqnr++;
321	}
322	return (seqnr);
323}
324
325/*
326 * main entry point.
327 */
328
329void
330dumpsys(struct dumperinfo *di)
331{
332	Elf64_Ehdr ehdr;
333	uint64_t dumpsize;
334	off_t hdrgap;
335	size_t hdrsz;
336	int error, status;
337
338	bzero(&ehdr, sizeof(ehdr));
339	ehdr.e_ident[EI_MAG0] = ELFMAG0;
340	ehdr.e_ident[EI_MAG1] = ELFMAG1;
341	ehdr.e_ident[EI_MAG2] = ELFMAG2;
342	ehdr.e_ident[EI_MAG3] = ELFMAG3;
343	ehdr.e_ident[EI_CLASS] = ELFCLASS64;
344#if BYTE_ORDER == LITTLE_ENDIAN
345	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
346#else
347	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
348#endif
349	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
350	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
351	ehdr.e_type = ET_CORE;
352	ehdr.e_machine = EM_IA_64;
353	ehdr.e_entry = (minidump) ? (uintptr_t)bootinfo :
354	    ia64_tpa((uintptr_t)bootinfo);
355	ehdr.e_phoff = sizeof(ehdr);
356	ehdr.e_flags = (minidump) ? 0 : EF_IA_64_ABSOLUTE; /* XXX misuse? */
357	ehdr.e_ehsize = sizeof(ehdr);
358	ehdr.e_phentsize = sizeof(Elf64_Phdr);
359	ehdr.e_shentsize = sizeof(Elf64_Shdr);
360
361	/* Calculate dump size. */
362	dumpsize = 0L;
363	status = (minidump) ? virt_foreach(virt_cb_size, &dumpsize) :
364	    phys_foreach(phys_cb_size, &dumpsize);
365	if (status < 0) {
366		error = -status;
367		goto fail;
368	}
369	ehdr.e_phnum = status;
370	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
371	fileofs = (minidump) ? round_page(hdrsz) : MD_ALIGN(hdrsz);
372	dumpsize += fileofs;
373	hdrgap = fileofs - DEV_ALIGN(hdrsz);
374
375	/* Determine dump offset on device. */
376	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
377		error = ENOSPC;
378		goto fail;
379	}
380	dumplo = di->mediaoffset + di->mediasize - dumpsize;
381	dumplo -= sizeof(kdh) * 2;
382
383	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize);
384
385	printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20,
386	    ehdr.e_phnum);
387
388	/* Dump leader */
389	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
390	if (error)
391		goto fail;
392	dumplo += sizeof(kdh);
393
394	/* Dump ELF header */
395	error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
396	if (error)
397		goto fail;
398
399	/* Dump program headers */
400	status = (minidump) ? virt_foreach(virt_cb_dumphdr, di) :
401	    phys_foreach(phys_cb_dumphdr, di);
402	if (status < 0) {
403		error = -status;
404		goto fail;
405	}
406	buf_flush(di);
407
408	/*
409	 * All headers are written using blocked I/O, so we know the
410	 * current offset is (still) block aligned. Skip the alignment
411	 * in the file to have the segment contents aligned at page
412	 * boundary. For physical dumps, it's the EFI page size (= 4K).
413	 * For minidumps it's the kernel's page size (= 8K).
414	 */
415	dumplo += hdrgap;
416
417	/* Dump memory chunks (updates dumplo) */
418	status = (minidump) ? virt_foreach(virt_cb_dumpdata, di) :
419	    phys_foreach(phys_cb_dumpdata, di);
420	if (status < 0) {
421		error = -status;
422		goto fail;
423	}
424
425	/* Dump trailer */
426	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
427	if (error)
428		goto fail;
429
430	/* Signal completion, signoff and exit stage left. */
431	dump_write(di, NULL, 0, 0, 0);
432	printf("\nDump complete\n");
433	return;
434
435 fail:
436	if (error == ECANCELED)
437		printf("\nDump aborted\n");
438	else
439		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
440}
441