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