minidump_machdep.c revision 331017
1214903Sgonzo/*-
2330897Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330897Seadler *
4214903Sgonzo * Copyright (c) 2010 Oleksandr Tymoshenko <gonzo@freebsd.org>
5214903Sgonzo * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
6214903Sgonzo * All rights reserved.
7214903Sgonzo *
8214903Sgonzo * Redistribution and use in source and binary forms, with or without
9214903Sgonzo * modification, are permitted provided that the following conditions
10214903Sgonzo * are met:
11214903Sgonzo *
12214903Sgonzo * 1. Redistributions of source code must retain the above copyright
13214903Sgonzo *    notice, this list of conditions and the following disclaimer.
14214903Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
15214903Sgonzo *    notice, this list of conditions and the following disclaimer in the
16214903Sgonzo *    documentation and/or other materials provided with the distribution.
17214903Sgonzo *
18214903Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19214903Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20214903Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21214903Sgonzo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22214903Sgonzo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23214903Sgonzo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24214903Sgonzo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25214903Sgonzo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26214903Sgonzo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27214903Sgonzo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28214903Sgonzo *
29214903Sgonzo * from: FreeBSD: src/sys/arm/arm/minidump_machdep.c v214223
30214903Sgonzo */
31214903Sgonzo
32214903Sgonzo#include <sys/cdefs.h>
33214903Sgonzo__FBSDID("$FreeBSD: stable/11/sys/mips/mips/minidump_machdep.c 331017 2018-03-15 19:08:33Z kevans $");
34214903Sgonzo
35214903Sgonzo#include <sys/param.h>
36214903Sgonzo#include <sys/systm.h>
37214903Sgonzo#include <sys/conf.h>
38214903Sgonzo#include <sys/cons.h>
39214903Sgonzo#include <sys/kernel.h>
40214903Sgonzo#include <sys/kerneldump.h>
41214903Sgonzo#include <sys/msgbuf.h>
42331017Skevans#include <sys/vmmeter.h>
43214903Sgonzo#include <vm/vm.h>
44214903Sgonzo#include <vm/pmap.h>
45214903Sgonzo#include <machine/atomic.h>
46214903Sgonzo#include <machine/elf.h>
47214903Sgonzo#include <machine/md_var.h>
48214903Sgonzo#include <machine/vmparam.h>
49214903Sgonzo#include <machine/minidump.h>
50214903Sgonzo#include <machine/cache.h>
51214903Sgonzo
52214903SgonzoCTASSERT(sizeof(struct kerneldumpheader) == 512);
53214903Sgonzo
54214903Sgonzo/*
55214903Sgonzo * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
56214903Sgonzo * is to protect us from metadata and to protect metadata from us.
57214903Sgonzo */
58214903Sgonzo#define	SIZEOF_METADATA		(64*1024)
59214903Sgonzo
60214903Sgonzouint32_t *vm_page_dump;
61214903Sgonzoint vm_page_dump_size;
62214903Sgonzo
63214903Sgonzostatic struct kerneldumpheader kdh;
64214903Sgonzostatic off_t dumplo;
65214903Sgonzostatic off_t origdumplo;
66214903Sgonzo
67214903Sgonzo/* Handle chunked writes. */
68214903Sgonzostatic uint64_t counter, progress;
69214903Sgonzo/* Just auxiliary bufffer */
70214903Sgonzostatic char tmpbuffer[PAGE_SIZE];
71214903Sgonzo
72214903Sgonzoextern pd_entry_t *kernel_segmap;
73214903Sgonzo
74214903SgonzoCTASSERT(sizeof(*vm_page_dump) == 4);
75214903Sgonzo
76214903Sgonzostatic int
77214903Sgonzois_dumpable(vm_paddr_t pa)
78214903Sgonzo{
79214903Sgonzo	int i;
80214903Sgonzo
81214903Sgonzo	for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
82214903Sgonzo		if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
83214903Sgonzo			return (1);
84214903Sgonzo	}
85214903Sgonzo	return (0);
86214903Sgonzo}
87214903Sgonzo
88216148Sjchandravoid
89214903Sgonzodump_add_page(vm_paddr_t pa)
90214903Sgonzo{
91214903Sgonzo	int idx, bit;
92214903Sgonzo
93214903Sgonzo	pa >>= PAGE_SHIFT;
94214903Sgonzo	idx = pa >> 5;		/* 2^5 = 32 */
95214903Sgonzo	bit = pa & 31;
96214903Sgonzo	atomic_set_int(&vm_page_dump[idx], 1ul << bit);
97214903Sgonzo}
98214903Sgonzo
99216148Sjchandravoid
100214903Sgonzodump_drop_page(vm_paddr_t pa)
101214903Sgonzo{
102214903Sgonzo	int idx, bit;
103214903Sgonzo
104214903Sgonzo	pa >>= PAGE_SHIFT;
105214903Sgonzo	idx = pa >> 5;		/* 2^5 = 32 */
106214903Sgonzo	bit = pa & 31;
107214903Sgonzo	atomic_clear_int(&vm_page_dump[idx], 1ul << bit);
108214903Sgonzo}
109214903Sgonzo
110214903Sgonzo#define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
111214903Sgonzo
112214903Sgonzostatic int
113214903Sgonzowrite_buffer(struct dumperinfo *di, char *ptr, size_t sz)
114214903Sgonzo{
115214903Sgonzo	size_t len;
116214903Sgonzo	int error, c;
117214903Sgonzo	u_int maxdumpsz;
118214903Sgonzo
119214903Sgonzo	maxdumpsz = di->maxiosize;
120214903Sgonzo
121214903Sgonzo	if (maxdumpsz == 0)	/* seatbelt */
122214903Sgonzo		maxdumpsz = PAGE_SIZE;
123214903Sgonzo
124214903Sgonzo	error = 0;
125214903Sgonzo
126214903Sgonzo	while (sz) {
127214903Sgonzo		len = min(maxdumpsz, sz);
128214903Sgonzo		counter += len;
129214903Sgonzo		progress -= len;
130214903Sgonzo
131214903Sgonzo		if (counter >> 22) {
132214903Sgonzo			printf(" %jd", PG2MB(progress >> PAGE_SHIFT));
133214903Sgonzo			counter &= (1<<22) - 1;
134214903Sgonzo		}
135214903Sgonzo
136214903Sgonzo		if (ptr) {
137214903Sgonzo			error = dump_write(di, ptr, 0, dumplo, len);
138214903Sgonzo			if (error)
139214903Sgonzo				return (error);
140214903Sgonzo			dumplo += len;
141214903Sgonzo			ptr += len;
142214903Sgonzo			sz -= len;
143214903Sgonzo		} else {
144214903Sgonzo			panic("pa is not supported");
145214903Sgonzo		}
146214903Sgonzo
147214903Sgonzo		/* Check for user abort. */
148214903Sgonzo		c = cncheckc();
149214903Sgonzo		if (c == 0x03)
150214903Sgonzo			return (ECANCELED);
151214903Sgonzo		if (c != -1)
152214903Sgonzo			printf(" (CTRL-C to abort) ");
153214903Sgonzo	}
154214903Sgonzo
155214903Sgonzo	return (0);
156214903Sgonzo}
157214903Sgonzo
158272766Smarkjint
159214903Sgonzominidumpsys(struct dumperinfo *di)
160214903Sgonzo{
161214903Sgonzo	struct minidumphdr mdhdr;
162214903Sgonzo	uint64_t dumpsize;
163214903Sgonzo	uint32_t ptesize;
164214903Sgonzo	uint32_t bits;
165214903Sgonzo	vm_paddr_t pa;
166214903Sgonzo	vm_offset_t prev_pte = 0;
167214903Sgonzo	uint32_t count = 0;
168214903Sgonzo	vm_offset_t va;
169214903Sgonzo	pt_entry_t *pte;
170214903Sgonzo	int i, bit, error;
171214903Sgonzo	void *dump_va;
172214903Sgonzo
173214903Sgonzo	/* Flush cache */
174214903Sgonzo	mips_dcache_wbinv_all();
175214903Sgonzo
176214903Sgonzo	counter = 0;
177214903Sgonzo	/* Walk page table pages, set bits in vm_page_dump */
178214903Sgonzo	ptesize = 0;
179214903Sgonzo
180214903Sgonzo	for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) {
181214903Sgonzo		ptesize += PAGE_SIZE;
182214903Sgonzo		pte = pmap_pte(kernel_pmap, va);
183214903Sgonzo		KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va));
184214903Sgonzo		for (i = 0; i < NPTEPG; i++) {
185214903Sgonzo			if (pte_test(&pte[i], PTE_V)) {
186214903Sgonzo				pa = TLBLO_PTE_TO_PA(pte[i]);
187214903Sgonzo				if (is_dumpable(pa))
188214903Sgonzo					dump_add_page(pa);
189214903Sgonzo			}
190214903Sgonzo		}
191214903Sgonzo	}
192214903Sgonzo
193214903Sgonzo	/*
194214903Sgonzo	 * Now mark pages from 0 to phys_avail[0], that's where kernel
195214903Sgonzo	 * and pages allocated by pmap_steal reside
196214903Sgonzo	 */
197214903Sgonzo	for (pa = 0; pa < phys_avail[0]; pa += PAGE_SIZE) {
198214903Sgonzo		if (is_dumpable(pa))
199214903Sgonzo			dump_add_page(pa);
200214903Sgonzo	}
201214903Sgonzo
202214903Sgonzo	/* Calculate dump size. */
203214903Sgonzo	dumpsize = ptesize;
204214903Sgonzo	dumpsize += round_page(msgbufp->msg_size);
205214903Sgonzo	dumpsize += round_page(vm_page_dump_size);
206214903Sgonzo
207214903Sgonzo	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
208214903Sgonzo		bits = vm_page_dump[i];
209214903Sgonzo		while (bits) {
210214903Sgonzo			bit = ffs(bits) - 1;
211214903Sgonzo			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
212214903Sgonzo			    bit) * PAGE_SIZE;
213214903Sgonzo			/* Clear out undumpable pages now if needed */
214214903Sgonzo			if (is_dumpable(pa))
215214903Sgonzo				dumpsize += PAGE_SIZE;
216214903Sgonzo			else
217214903Sgonzo				dump_drop_page(pa);
218214903Sgonzo			bits &= ~(1ul << bit);
219214903Sgonzo		}
220214903Sgonzo	}
221214903Sgonzo
222214903Sgonzo	dumpsize += PAGE_SIZE;
223214903Sgonzo
224214903Sgonzo	/* Determine dump offset on device. */
225214903Sgonzo	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
226214903Sgonzo		error = ENOSPC;
227214903Sgonzo		goto fail;
228214903Sgonzo	}
229214903Sgonzo
230214903Sgonzo	origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize;
231214903Sgonzo	dumplo -= sizeof(kdh) * 2;
232214903Sgonzo	progress = dumpsize;
233214903Sgonzo
234214903Sgonzo	/* Initialize mdhdr */
235214903Sgonzo	bzero(&mdhdr, sizeof(mdhdr));
236214903Sgonzo	strcpy(mdhdr.magic, MINIDUMP_MAGIC);
237214903Sgonzo	mdhdr.version = MINIDUMP_VERSION;
238214903Sgonzo	mdhdr.msgbufsize = msgbufp->msg_size;
239214903Sgonzo	mdhdr.bitmapsize = vm_page_dump_size;
240214903Sgonzo	mdhdr.ptesize = ptesize;
241214903Sgonzo	mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
242214903Sgonzo
243214903Sgonzo	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize,
244214903Sgonzo	    di->blocksize);
245214903Sgonzo
246214903Sgonzo	printf("Physical memory: %ju MB\n",
247214903Sgonzo	    (uintmax_t)ptoa((uintmax_t)physmem) / 1048576);
248214903Sgonzo	printf("Dumping %llu MB:", (long long)dumpsize >> 20);
249214903Sgonzo
250214903Sgonzo	/* Dump leader */
251214903Sgonzo	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
252214903Sgonzo	if (error)
253214903Sgonzo		goto fail;
254214903Sgonzo	dumplo += sizeof(kdh);
255214903Sgonzo
256214903Sgonzo	/* Dump my header */
257214903Sgonzo	bzero(tmpbuffer, sizeof(tmpbuffer));
258214903Sgonzo	bcopy(&mdhdr, tmpbuffer, sizeof(mdhdr));
259214903Sgonzo	error = write_buffer(di, tmpbuffer, PAGE_SIZE);
260214903Sgonzo	if (error)
261214903Sgonzo		goto fail;
262214903Sgonzo
263214903Sgonzo	/* Dump msgbuf up front */
264214903Sgonzo	error = write_buffer(di, (char *)msgbufp->msg_ptr,
265214903Sgonzo	    round_page(msgbufp->msg_size));
266214903Sgonzo	if (error)
267214903Sgonzo		goto fail;
268214903Sgonzo
269214903Sgonzo	/* Dump bitmap */
270214903Sgonzo	error = write_buffer(di, (char *)vm_page_dump,
271214903Sgonzo	    round_page(vm_page_dump_size));
272214903Sgonzo	if (error)
273214903Sgonzo		goto fail;
274214903Sgonzo
275214903Sgonzo	/* Dump kernel page table pages */
276214903Sgonzo	for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) {
277214903Sgonzo		pte = pmap_pte(kernel_pmap, va);
278214903Sgonzo		KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va));
279214903Sgonzo		if (!count) {
280214903Sgonzo			prev_pte = (vm_offset_t)pte;
281214903Sgonzo			count++;
282214903Sgonzo		}
283214903Sgonzo		else {
284214903Sgonzo			if ((vm_offset_t)pte == (prev_pte + count * PAGE_SIZE))
285214903Sgonzo				count++;
286214903Sgonzo			else {
287214903Sgonzo				error = write_buffer(di, (char*)prev_pte,
288214903Sgonzo				    count * PAGE_SIZE);
289214903Sgonzo				if (error)
290214903Sgonzo					goto fail;
291214903Sgonzo				count = 1;
292214903Sgonzo				prev_pte = (vm_offset_t)pte;
293214903Sgonzo			}
294214903Sgonzo		}
295214903Sgonzo	}
296214903Sgonzo
297214903Sgonzo	if (count) {
298214903Sgonzo		error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE);
299214903Sgonzo		if (error)
300214903Sgonzo			goto fail;
301214903Sgonzo		count = 0;
302214903Sgonzo		prev_pte = 0;
303214903Sgonzo	}
304214903Sgonzo
305214903Sgonzo	/* Dump memory chunks  page by page*/
306214903Sgonzo	for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
307214903Sgonzo		bits = vm_page_dump[i];
308214903Sgonzo		while (bits) {
309214903Sgonzo			bit = ffs(bits) - 1;
310214903Sgonzo			pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
311214903Sgonzo			    bit) * PAGE_SIZE;
312214903Sgonzo			dump_va = pmap_kenter_temporary(pa, 0);
313214903Sgonzo			error = write_buffer(di, dump_va, PAGE_SIZE);
314214903Sgonzo			if (error)
315214903Sgonzo				goto fail;
316214903Sgonzo			pmap_kenter_temporary_free(pa);
317214903Sgonzo			bits &= ~(1ul << bit);
318214903Sgonzo		}
319214903Sgonzo	}
320214903Sgonzo
321214903Sgonzo	/* Dump trailer */
322214903Sgonzo	error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
323214903Sgonzo	if (error)
324214903Sgonzo		goto fail;
325214903Sgonzo	dumplo += sizeof(kdh);
326214903Sgonzo
327214903Sgonzo	/* Signal completion, signoff and exit stage left. */
328214903Sgonzo	dump_write(di, NULL, 0, 0, 0);
329214903Sgonzo	printf("\nDump complete\n");
330272766Smarkj	return (0);
331214903Sgonzo
332214903Sgonzofail:
333214903Sgonzo	if (error < 0)
334214903Sgonzo		error = -error;
335214903Sgonzo
336214903Sgonzo	if (error == ECANCELED)
337214903Sgonzo		printf("\nDump aborted\n");
338214903Sgonzo	else if (error == ENOSPC)
339214903Sgonzo		printf("\nDump failed. Partition too small.\n");
340214903Sgonzo	else
341214903Sgonzo		printf("\n** DUMP FAILED (ERROR %d) **\n", error);
342272766Smarkj	return (error);
343214903Sgonzo}
344