1139790Simp/*-
293647Smarcel * Copyright (c) 2002 Marcel Moolenaar
393647Smarcel * All rights reserved.
493647Smarcel *
593647Smarcel * Redistribution and use in source and binary forms, with or without
693647Smarcel * modification, are permitted provided that the following conditions
793647Smarcel * are met:
893647Smarcel *
993647Smarcel * 1. Redistributions of source code must retain the above copyright
1093647Smarcel *    notice, this list of conditions and the following disclaimer.
1193647Smarcel * 2. Redistributions in binary form must reproduce the above copyright
1293647Smarcel *    notice, this list of conditions and the following disclaimer in the
1393647Smarcel *    documentation and/or other materials provided with the distribution.
1493647Smarcel *
1593647Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1693647Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1793647Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1893647Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1993647Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2093647Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2193647Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2293647Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2393647Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2493647Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2593647Smarcel */
2693647Smarcel
27135453Smarcel#include <sys/cdefs.h>
28135453Smarcel__FBSDID("$FreeBSD$");
29135453Smarcel
30221173Sattilio#include "opt_watchdog.h"
31221173Sattilio
3293647Smarcel#include <sys/param.h>
3393647Smarcel#include <sys/systm.h>
3493647Smarcel#include <sys/conf.h>
35105079Smarcel#include <sys/cons.h>
36270296Semaste#include <sys/efi.h>
3793647Smarcel#include <sys/kernel.h>
3893647Smarcel#include <sys/kerneldump.h>
39221173Sattilio#ifdef SW_WATCHDOG
40221173Sattilio#include <sys/watchdog.h>
41221173Sattilio#endif
4293647Smarcel#include <vm/vm.h>
4393647Smarcel#include <vm/pmap.h>
44224668Smarcel#include <machine/bootinfo.h>
4593647Smarcel#include <machine/elf.h>
4693647Smarcel#include <machine/md_var.h>
4793647Smarcel
4893717SmarcelCTASSERT(sizeof(struct kerneldumpheader) == 512);
4993717Smarcel
50107963Smarcel/*
51107963Smarcel * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
52107963Smarcel * is to protect us from metadata and to protect metadata from us.
53107963Smarcel */
54107963Smarcel#define	SIZEOF_METADATA		(64*1024)
55107963Smarcel
5693647Smarcel#define	MD_ALIGN(x)	(((off_t)(x) + EFI_PAGE_MASK) & ~EFI_PAGE_MASK)
5793933Smarcel#define	DEV_ALIGN(x)	(((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1))
5893647Smarcel
59268192Smarcelstatic int minidump = 0;
60268192SmarcelTUNABLE_INT("debug.minidump", &minidump);
61268192SmarcelSYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RW, &minidump, 0,
62268192Smarcel    "Enable mini crash dumps");
6393647Smarcel
6493647Smarcelstatic struct kerneldumpheader kdh;
6593647Smarcelstatic off_t dumplo, fileofs;
6693647Smarcel
6793647Smarcel/* Handle buffered writes. */
6893647Smarcelstatic char buffer[DEV_BSIZE];
6993647Smarcelstatic size_t fragsz;
7093647Smarcel
7193647Smarcelstatic int
7293647Smarcelbuf_write(struct dumperinfo *di, char *ptr, size_t sz)
7393647Smarcel{
7493647Smarcel	size_t len;
7593647Smarcel	int error;
7693647Smarcel
7793647Smarcel	while (sz) {
7893647Smarcel		len = DEV_BSIZE - fragsz;
7993647Smarcel		if (len > sz)
8093647Smarcel			len = sz;
8193647Smarcel		bcopy(ptr, buffer + fragsz, len);
8293647Smarcel		fragsz += len;
8393647Smarcel		ptr += len;
8493647Smarcel		sz -= len;
8593647Smarcel		if (fragsz == DEV_BSIZE) {
86175768Sru			error = dump_write(di, buffer, 0, dumplo,
8793647Smarcel			    DEV_BSIZE);
8893647Smarcel			if (error)
89268192Smarcel				return (error);
9093647Smarcel			dumplo += DEV_BSIZE;
9193647Smarcel			fragsz = 0;
9293647Smarcel		}
9393647Smarcel	}
9493647Smarcel
9593647Smarcel	return (0);
9693647Smarcel}
9793647Smarcel
9893647Smarcelstatic int
9993647Smarcelbuf_flush(struct dumperinfo *di)
10093647Smarcel{
10193647Smarcel	int error;
10293647Smarcel
10393647Smarcel	if (fragsz == 0)
10493647Smarcel		return (0);
10593647Smarcel
106175768Sru	error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE);
10793647Smarcel	dumplo += DEV_BSIZE;
108147740Smarcel	fragsz = 0;
10993647Smarcel	return (error);
11093647Smarcel}
11193647Smarcel
112268192Smarcel/*
113268192Smarcel * Physical dump support
114268192Smarcel */
115268192Smarcel
116268192Smarceltypedef int phys_callback_t(struct efi_md*, int, void*);
117268192Smarcel
11893647Smarcelstatic int
119268192Smarcelphys_cb_dumpdata(struct efi_md *mdp, int seqnr, void *arg)
12093647Smarcel{
12193647Smarcel	struct dumperinfo *di = (struct dumperinfo*)arg;
12293647Smarcel	vm_offset_t pa;
12393647Smarcel	uint64_t pgs;
12494642Smarcel	size_t counter, sz;
125105079Smarcel	int c, error, twiddle;
12693647Smarcel
127135453Smarcel	error = 0;	/* catch case in which mdp->md_pages is 0 */
12894642Smarcel	counter = 0;	/* Update twiddle every 16MB */
12993712Smarcel	twiddle = 0;
130135453Smarcel	pgs = mdp->md_pages;
131135453Smarcel	pa = IA64_PHYS_TO_RR7(mdp->md_phys);
13293712Smarcel
13394642Smarcel	printf("  chunk %d: %ld pages ", seqnr, (long)pgs);
13493712Smarcel
13593647Smarcel	while (pgs) {
13693647Smarcel		sz = (pgs > (DFLTPHYS >> EFI_PAGE_SHIFT))
13793647Smarcel		    ? DFLTPHYS : pgs << EFI_PAGE_SHIFT;
13894642Smarcel		counter += sz;
13994642Smarcel		if (counter >> 24) {
14094642Smarcel			printf("%c\b", "|/-\\"[twiddle++ & 3]);
14194642Smarcel			counter &= (1<<24) - 1;
14294642Smarcel		}
143221173Sattilio#ifdef SW_WATCHDOG
144221173Sattilio		wdog_kern_pat(WD_LASTVAL);
145221173Sattilio#endif
146175768Sru		error = dump_write(di, (void*)pa, 0, dumplo, sz);
14793647Smarcel		if (error)
14893647Smarcel			break;
14993647Smarcel		dumplo += sz;
15093647Smarcel		pgs -= sz >> EFI_PAGE_SHIFT;
15193647Smarcel		pa += sz;
152105079Smarcel
153105079Smarcel		/* Check for user abort. */
154105079Smarcel		c = cncheckc();
155105079Smarcel		if (c == 0x03)
156105079Smarcel			return (ECANCELED);
157105079Smarcel		if (c != -1)
158105079Smarcel			printf("(CTRL-C to abort)  ");
15993647Smarcel	}
16093712Smarcel	printf("... %s\n", (error) ? "fail" : "ok");
16193647Smarcel	return (error);
16293647Smarcel}
16393647Smarcel
16493647Smarcelstatic int
165268192Smarcelphys_cb_dumphdr(struct efi_md *mdp, int seqnr, void *arg)
16693647Smarcel{
16793647Smarcel	struct dumperinfo *di = (struct dumperinfo*)arg;
16893647Smarcel	Elf64_Phdr phdr;
16993647Smarcel	int error;
17093647Smarcel
17193647Smarcel	bzero(&phdr, sizeof(phdr));
17293647Smarcel	phdr.p_type = PT_LOAD;
17393647Smarcel	phdr.p_flags = PF_R;			/* XXX */
17493647Smarcel	phdr.p_offset = fileofs;
175135453Smarcel	phdr.p_vaddr = (uintptr_t)mdp->md_virt;	/* XXX probably bogus. */
176135453Smarcel	phdr.p_paddr = mdp->md_phys;
177135453Smarcel	phdr.p_filesz = mdp->md_pages << EFI_PAGE_SHIFT;
178135453Smarcel	phdr.p_memsz = mdp->md_pages << EFI_PAGE_SHIFT;
17993647Smarcel	phdr.p_align = EFI_PAGE_SIZE;
18093647Smarcel
181105591Smarcel	error = buf_write(di, (char*)&phdr, sizeof(phdr));
18293647Smarcel	fileofs += phdr.p_filesz;
18393647Smarcel	return (error);
18493647Smarcel}
18593647Smarcel
18693647Smarcelstatic int
187268192Smarcelphys_cb_size(struct efi_md *mdp, int seqnr, void *arg)
18893647Smarcel{
18993647Smarcel	uint64_t *sz = (uint64_t*)arg;
19093647Smarcel
191135453Smarcel	*sz += (uint64_t)mdp->md_pages << EFI_PAGE_SHIFT;
19293647Smarcel	return (0);
19393647Smarcel}
19493647Smarcel
19593647Smarcelstatic int
196268192Smarcelphys_foreach(phys_callback_t cb, void *arg)
19793647Smarcel{
198135453Smarcel	struct efi_md *mdp;
199135453Smarcel	int error, seqnr;
20093647Smarcel
201135453Smarcel	seqnr = 0;
202135453Smarcel	mdp = efi_md_first();
203135453Smarcel	while (mdp != NULL) {
204224668Smarcel		if (mdp->md_type == EFI_MD_TYPE_FREE ||
205246712Smarcel		    mdp->md_type == EFI_MD_TYPE_DATA ||
206246712Smarcel		    mdp->md_type == EFI_MD_TYPE_CODE ||
207246712Smarcel		    mdp->md_type == EFI_MD_TYPE_BS_DATA ||
208246712Smarcel		    mdp->md_type == EFI_MD_TYPE_BS_CODE) {
20993647Smarcel			error = (*cb)(mdp, seqnr++, arg);
21093647Smarcel			if (error)
21193647Smarcel				return (-error);
21293647Smarcel		}
213135453Smarcel		mdp = efi_md_next(mdp);
21493647Smarcel	}
21593647Smarcel	return (seqnr);
21693647Smarcel}
21793647Smarcel
218268192Smarcel/*
219268192Smarcel * Virtual dump (aka minidump) support
220268192Smarcel */
221268192Smarcel
222268192Smarceltypedef int virt_callback_t(vm_offset_t, vm_size_t, int, void*);
223268192Smarcel
224268192Smarcelstatic int
225268192Smarcelvirt_cb_size(vm_offset_t va, vm_size_t sz, int seqnr, void *arg)
226268192Smarcel{
227268192Smarcel	uint64_t *dumpsize = (uint64_t *)arg;
228268192Smarcel
229268192Smarcel	*dumpsize += sz;
230268192Smarcel	return (0);
231268192Smarcel}
232268192Smarcel
233268192Smarcelstatic int
234268192Smarcelvirt_cb_dumphdr(vm_offset_t va, vm_size_t sz, int seqnr, void *arg)
235268192Smarcel{
236268192Smarcel	struct dumperinfo *di = (struct dumperinfo *)arg;
237268192Smarcel	Elf64_Phdr phdr;
238268192Smarcel	int error;
239268192Smarcel
240268192Smarcel	bzero(&phdr, sizeof(phdr));
241268192Smarcel	phdr.p_type = PT_LOAD;
242268192Smarcel	phdr.p_flags = PF_R;			/* XXX */
243268192Smarcel	phdr.p_offset = fileofs;
244268192Smarcel	phdr.p_vaddr = va;
245268192Smarcel	phdr.p_paddr = ~0UL;
246268192Smarcel	phdr.p_filesz = sz;
247268192Smarcel	phdr.p_memsz = sz;
248268192Smarcel	phdr.p_align = PAGE_SIZE;
249268192Smarcel
250268192Smarcel	error = buf_write(di, (char*)&phdr, sizeof(phdr));
251268192Smarcel	fileofs += phdr.p_filesz;
252268192Smarcel	return (error);
253268192Smarcel}
254268192Smarcel
255268192Smarcelstatic int
256268192Smarcelvirt_cb_dumpdata(vm_offset_t va, vm_size_t sz, int seqnr, void *arg)
257268192Smarcel{
258268192Smarcel	struct dumperinfo *di = (struct dumperinfo *)arg;
259268192Smarcel	size_t counter, iosz;
260268192Smarcel	int c, error, twiddle;
261268192Smarcel
262268192Smarcel	error = 0;	/* catch case in which pgs is 0 */
263268192Smarcel	counter = 0;	/* Update twiddle every 16MB */
264268192Smarcel	twiddle = 0;
265268192Smarcel
266268192Smarcel	printf("  chunk %d: %ld pages ", seqnr, atop(sz));
267268192Smarcel
268268192Smarcel	while (sz) {
269268192Smarcel		iosz = (sz > DFLTPHYS) ? DFLTPHYS : sz;
270268192Smarcel		counter += iosz;
271268192Smarcel		if (counter >> 24) {
272268192Smarcel			printf("%c\b", "|/-\\"[twiddle++ & 3]);
273268192Smarcel			counter &= (1<<24) - 1;
274268192Smarcel		}
275268192Smarcel#ifdef SW_WATCHDOG
276268192Smarcel		wdog_kern_pat(WD_LASTVAL);
277268192Smarcel#endif
278268192Smarcel		error = dump_write(di, (void*)va, 0, dumplo, iosz);
279268192Smarcel		if (error)
280268192Smarcel			break;
281268192Smarcel		dumplo += iosz;
282268192Smarcel		sz -= iosz;
283268192Smarcel		va += iosz;
284268192Smarcel
285268192Smarcel		/* Check for user abort. */
286268192Smarcel		c = cncheckc();
287268192Smarcel		if (c == 0x03)
288268192Smarcel			return (ECANCELED);
289268192Smarcel		if (c != -1)
290268192Smarcel			printf("(CTRL-C to abort)  ");
291268192Smarcel	}
292268192Smarcel	printf("... %s\n", (error) ? "fail" : "ok");
293268192Smarcel	return (error);
294268192Smarcel}
295268192Smarcel
296268192Smarcelstatic int
297268192Smarcelvirt_foreach(virt_callback_t cb, void *arg)
298268192Smarcel{
299268192Smarcel	vm_offset_t va;
300268192Smarcel	vm_size_t sz;
301268192Smarcel	int error, seqnr;
302268192Smarcel
303268192Smarcel	seqnr = 0;
304268192Smarcel	while (1) {
305268192Smarcel		switch (seqnr) {
306268192Smarcel		case 0:
307268192Smarcel			va = IA64_PBVM_BASE;
308268192Smarcel			sz = round_page(bootinfo->bi_kernend) - va;
309268192Smarcel			break;
310268192Smarcel		default:
311268192Smarcel			va = 0;
312268192Smarcel			sz = 0;
313268192Smarcel			break;
314268192Smarcel		}
315268192Smarcel		if (va == 0 && sz == 0)
316268192Smarcel			break;
317268192Smarcel		error = (*cb)(va, sz, seqnr, arg);
318268192Smarcel		if (error)
319268192Smarcel			return (-error);
320268192Smarcel		seqnr++;
321268192Smarcel	}
322268192Smarcel	return (seqnr);
323268192Smarcel}
324268192Smarcel
325268192Smarcel/*
326268192Smarcel * main entry point.
327268192Smarcel */
328268192Smarcel
32993647Smarcelvoid
33093647Smarceldumpsys(struct dumperinfo *di)
33193647Smarcel{
33293647Smarcel	Elf64_Ehdr ehdr;
33393647Smarcel	uint64_t dumpsize;
33493933Smarcel	off_t hdrgap;
33593647Smarcel	size_t hdrsz;
336268192Smarcel	int error, status;
33793647Smarcel
33893647Smarcel	bzero(&ehdr, sizeof(ehdr));
33993647Smarcel	ehdr.e_ident[EI_MAG0] = ELFMAG0;
34093647Smarcel	ehdr.e_ident[EI_MAG1] = ELFMAG1;
34193647Smarcel	ehdr.e_ident[EI_MAG2] = ELFMAG2;
34293647Smarcel	ehdr.e_ident[EI_MAG3] = ELFMAG3;
34393647Smarcel	ehdr.e_ident[EI_CLASS] = ELFCLASS64;
34493717Smarcel#if BYTE_ORDER == LITTLE_ENDIAN
34593647Smarcel	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
34693717Smarcel#else
34793717Smarcel	ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
34893717Smarcel#endif
34993647Smarcel	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
35093647Smarcel	ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE;	/* XXX big picture? */
35193647Smarcel	ehdr.e_type = ET_CORE;
35293647Smarcel	ehdr.e_machine = EM_IA_64;
353268192Smarcel	ehdr.e_entry = (minidump) ? (uintptr_t)bootinfo :
354268192Smarcel	    ia64_tpa((uintptr_t)bootinfo);
35593647Smarcel	ehdr.e_phoff = sizeof(ehdr);
356268192Smarcel	ehdr.e_flags = (minidump) ? 0 : EF_IA_64_ABSOLUTE; /* XXX misuse? */
35793647Smarcel	ehdr.e_ehsize = sizeof(ehdr);
35893647Smarcel	ehdr.e_phentsize = sizeof(Elf64_Phdr);
35993647Smarcel	ehdr.e_shentsize = sizeof(Elf64_Shdr);
36093647Smarcel
36193647Smarcel	/* Calculate dump size. */
36293647Smarcel	dumpsize = 0L;
363268192Smarcel	status = (minidump) ? virt_foreach(virt_cb_size, &dumpsize) :
364268192Smarcel	    phys_foreach(phys_cb_size, &dumpsize);
365268192Smarcel	if (status < 0) {
366268192Smarcel		error = -status;
367268192Smarcel		goto fail;
368268192Smarcel	}
369268192Smarcel	ehdr.e_phnum = status;
37093647Smarcel	hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;
371268192Smarcel	fileofs = (minidump) ? round_page(hdrsz) : MD_ALIGN(hdrsz);
37293647Smarcel	dumpsize += fileofs;
37393933Smarcel	hdrgap = fileofs - DEV_ALIGN(hdrsz);
37493647Smarcel
37593647Smarcel	/* Determine dump offset on device. */
376107963Smarcel	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
377107963Smarcel		error = ENOSPC;
378107963Smarcel		goto fail;
379107963Smarcel	}
38093647Smarcel	dumplo = di->mediaoffset + di->mediasize - dumpsize;
38193647Smarcel	dumplo -= sizeof(kdh) * 2;
38293647Smarcel
383183527Speter	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize);
38493647Smarcel
38596899Smarcel	printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20,
38696899Smarcel	    ehdr.e_phnum);
38793647Smarcel
38893647Smarcel	/* Dump leader */
389175768Sru	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
39093647Smarcel	if (error)
39193647Smarcel		goto fail;
39293647Smarcel	dumplo += sizeof(kdh);
39393647Smarcel
39493647Smarcel	/* Dump ELF header */
39593647Smarcel	error = buf_write(di, (char*)&ehdr, sizeof(ehdr));
39693647Smarcel	if (error)
39793647Smarcel		goto fail;
39893647Smarcel
39993647Smarcel	/* Dump program headers */
400268192Smarcel	status = (minidump) ? virt_foreach(virt_cb_dumphdr, di) :
401268192Smarcel	    phys_foreach(phys_cb_dumphdr, di);
402268192Smarcel	if (status < 0) {
403268192Smarcel		error = -status;
40493647Smarcel		goto fail;
405268192Smarcel	}
40693647Smarcel	buf_flush(di);
40793647Smarcel
40893933Smarcel	/*
40993933Smarcel	 * All headers are written using blocked I/O, so we know the
410268192Smarcel	 * current offset is (still) block aligned. Skip the alignment
41193933Smarcel	 * in the file to have the segment contents aligned at page
412268192Smarcel	 * boundary. For physical dumps, it's the EFI page size (= 4K).
413268192Smarcel	 * For minidumps it's the kernel's page size (= 8K).
41493933Smarcel	 */
41593933Smarcel	dumplo += hdrgap;
41693933Smarcel
41794642Smarcel	/* Dump memory chunks (updates dumplo) */
418268192Smarcel	status = (minidump) ? virt_foreach(virt_cb_dumpdata, di) :
419268192Smarcel	    phys_foreach(phys_cb_dumpdata, di);
420268192Smarcel	if (status < 0) {
421268192Smarcel		error = -status;
42293647Smarcel		goto fail;
423268192Smarcel	}
42493647Smarcel
42593647Smarcel	/* Dump trailer */
426175768Sru	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
42793647Smarcel	if (error)
42893647Smarcel		goto fail;
42993647Smarcel
43093647Smarcel	/* Signal completion, signoff and exit stage left. */
431175768Sru	dump_write(di, NULL, 0, 0, 0);
43293647Smarcel	printf("\nDump complete\n");
43393647Smarcel	return;
43493647Smarcel
43593647Smarcel fail:
436105079Smarcel	if (error == ECANCELED)
437105079Smarcel		printf("\nDump aborted\n");
438105079Smarcel	else
439105079Smarcel		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
44093647Smarcel}
441