]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/minidump_machdep.c
zfs: merge openzfs/zfs@03e9caaec
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / minidump_machdep.c
1 /*-
2  * Copyright (c) 2006 Peter Wemm
3  * Copyright (c) 2015 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * This software was developed by Andrew Turner under
7  * sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 #include "opt_watchdog.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/cons.h>
39 #include <sys/kernel.h>
40 #include <sys/kerneldump.h>
41 #include <sys/msgbuf.h>
42 #include <sys/watchdog.h>
43 #include <sys/vmmeter.h>
44
45 #include <vm/vm.h>
46 #include <vm/vm_param.h>
47 #include <vm/vm_page.h>
48 #include <vm/vm_phys.h>
49 #include <vm/vm_dumpset.h>
50 #include <vm/pmap.h>
51
52 #include <machine/atomic.h>
53 #include <machine/md_var.h>
54 #include <machine/pte.h>
55 #include <machine/minidump.h>
56
57 CTASSERT(sizeof(struct kerneldumpheader) == 512);
58
59 static struct kerneldumpheader kdh;
60
61 /* Handle chunked writes. */
62 static size_t fragsz;
63 static void *dump_va;
64 static size_t dumpsize;
65
66 static uint64_t tmpbuffer[Ln_ENTRIES];
67
68 static int
69 blk_flush(struct dumperinfo *di)
70 {
71         int error;
72
73         if (fragsz == 0)
74                 return (0);
75
76         error = dump_append(di, dump_va, fragsz);
77         fragsz = 0;
78         return (error);
79 }
80
81 static int
82 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
83 {
84         size_t len;
85         int error, c;
86         u_int maxdumpsz;
87
88         maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE);
89         if (maxdumpsz == 0)     /* seatbelt */
90                 maxdumpsz = PAGE_SIZE;
91         error = 0;
92         if ((sz % PAGE_SIZE) != 0) {
93                 printf("size not page aligned\n");
94                 return (EINVAL);
95         }
96         if (ptr != NULL && pa != 0) {
97                 printf("cant have both va and pa!\n");
98                 return (EINVAL);
99         }
100         if ((((uintptr_t)pa) % PAGE_SIZE) != 0) {
101                 printf("address not page aligned %p\n", ptr);
102                 return (EINVAL);
103         }
104         if (ptr != NULL) {
105                 /*
106                  * If we're doing a virtual dump, flush any
107                  * pre-existing pa pages.
108                  */
109                 error = blk_flush(di);
110                 if (error)
111                         return (error);
112         }
113         while (sz) {
114                 len = maxdumpsz - fragsz;
115                 if (len > sz)
116                         len = sz;
117
118                 dumpsys_pb_progress(len);
119                 wdog_kern_pat(WD_LASTVAL);
120
121                 if (ptr) {
122                         error = dump_append(di, ptr, len);
123                         if (error)
124                                 return (error);
125                         ptr += len;
126                         sz -= len;
127                 } else {
128                         dump_va = (void *)PHYS_TO_DMAP(pa);
129                         fragsz += len;
130                         pa += len;
131                         sz -= len;
132                         error = blk_flush(di);
133                         if (error)
134                                 return (error);
135                 }
136
137                 /* Check for user abort. */
138                 c = cncheckc();
139                 if (c == 0x03)
140                         return (ECANCELED);
141                 if (c != -1)
142                         printf(" (CTRL-C to abort) ");
143         }
144
145         return (0);
146 }
147
148 int
149 cpu_minidumpsys(struct dumperinfo *di, const struct minidumpstate *state)
150 {
151         struct minidumphdr mdhdr;
152         struct msgbuf *mbp;
153         pd_entry_t *l0, *l1, l1e, *l2, l2e;
154         pt_entry_t *l3, l3e;
155         vm_offset_t va, kva_end;
156         vm_paddr_t pa;
157         uint32_t pmapsize;
158         int error, i, j, retry_count;
159
160         retry_count = 0;
161  retry:
162         retry_count++;
163         error = 0;
164         pmapsize = 0;
165
166         /* Snapshot the KVA upper bound in case it grows. */
167         kva_end = kernel_vm_end;
168
169         /*
170          * Walk the kernel page table pages, setting the active entries in the
171          * dump bitmap.
172          *
173          * NB: for a live dump, we may be racing with updates to the page
174          * tables, so care must be taken to read each entry only once.
175          */
176         for (va = VM_MIN_KERNEL_ADDRESS; va < kva_end; va += L2_SIZE) {
177                 pmapsize += PAGE_SIZE;
178                 if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3))
179                         continue;
180
181                 l1e = atomic_load_64(l1);
182                 l2e = atomic_load_64(l2);
183                 if ((l1e & ATTR_DESCR_MASK) == L1_BLOCK) {
184                         pa = PTE_TO_PHYS(l1e);
185                         for (i = 0; i < Ln_ENTRIES * Ln_ENTRIES;
186                             i++, pa += PAGE_SIZE)
187                                 if (vm_phys_is_dumpable(pa))
188                                         vm_page_dump_add(state->dump_bitset,
189                                             pa);
190                         pmapsize += (Ln_ENTRIES - 1) * PAGE_SIZE;
191                         va += L1_SIZE - L2_SIZE;
192                 } else if ((l2e & ATTR_DESCR_MASK) == L2_BLOCK) {
193                         pa = PTE_TO_PHYS(l2e);
194                         for (i = 0; i < Ln_ENTRIES; i++, pa += PAGE_SIZE) {
195                                 if (vm_phys_is_dumpable(pa))
196                                         vm_page_dump_add(state->dump_bitset,
197                                             pa);
198                         }
199                 } else if ((l2e & ATTR_DESCR_MASK) == L2_TABLE) {
200                         for (i = 0; i < Ln_ENTRIES; i++) {
201                                 l3e = atomic_load_64(&l3[i]);
202                                 if ((l3e & ATTR_DESCR_MASK) != L3_PAGE)
203                                         continue;
204                                 pa = PTE_TO_PHYS(l3e);
205                                 if (PHYS_IN_DMAP(pa) && vm_phys_is_dumpable(pa))
206                                         vm_page_dump_add(state->dump_bitset,
207                                             pa);
208                         }
209                 }
210         }
211
212         /* Calculate dump size. */
213         mbp = state->msgbufp;
214         dumpsize = pmapsize;
215         dumpsize += round_page(mbp->msg_size);
216         dumpsize += round_page(sizeof(dump_avail));
217         dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages));
218         VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) {
219                 if (PHYS_IN_DMAP(pa) && vm_phys_is_dumpable(pa))
220                         dumpsize += PAGE_SIZE;
221                 else
222                         vm_page_dump_drop(state->dump_bitset, pa);
223         }
224         dumpsize += PAGE_SIZE;
225
226         dumpsys_pb_init(dumpsize);
227
228         /* Initialize mdhdr */
229         bzero(&mdhdr, sizeof(mdhdr));
230         strcpy(mdhdr.magic, MINIDUMP_MAGIC);
231         mdhdr.version = MINIDUMP_VERSION;
232         mdhdr.msgbufsize = mbp->msg_size;
233         mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages));
234         mdhdr.pmapsize = pmapsize;
235         mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
236         mdhdr.dmapphys = DMAP_MIN_PHYSADDR;
237         mdhdr.dmapbase = DMAP_MIN_ADDRESS;
238         mdhdr.dmapend = DMAP_MAX_ADDRESS;
239         mdhdr.dumpavailsize = round_page(sizeof(dump_avail));
240 #if PAGE_SIZE == PAGE_SIZE_4K
241         mdhdr.flags = MINIDUMP_FLAG_PS_4K;
242 #elif PAGE_SIZE == PAGE_SIZE_16K
243         mdhdr.flags = MINIDUMP_FLAG_PS_16K;
244 #else
245 #error Unsupported page size
246 #endif
247
248         dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_AARCH64_VERSION,
249             dumpsize);
250
251         error = dump_start(di, &kdh);
252         if (error != 0)
253                 goto fail;
254
255         printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20,
256             ptoa((uintmax_t)physmem) / 1048576);
257
258         /* Dump my header */
259         bzero(&tmpbuffer, sizeof(tmpbuffer));
260         bcopy(&mdhdr, &tmpbuffer, sizeof(mdhdr));
261         error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
262         if (error)
263                 goto fail;
264
265         /* Dump msgbuf up front */
266         error = blk_write(di, mbp->msg_ptr, 0, round_page(mbp->msg_size));
267         if (error)
268                 goto fail;
269
270         /* Dump dump_avail */
271         _Static_assert(sizeof(dump_avail) <= sizeof(tmpbuffer),
272             "Large dump_avail not handled");
273         bzero(tmpbuffer, sizeof(tmpbuffer));
274         memcpy(tmpbuffer, dump_avail, sizeof(dump_avail));
275         error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
276         if (error)
277                 goto fail;
278
279         /* Dump bitmap */
280         error = blk_write(di, (char *)state->dump_bitset, 0,
281             round_page(BITSET_SIZE(vm_page_dump_pages)));
282         if (error)
283                 goto fail;
284
285         /* Dump kernel page directory pages */
286         bzero(&tmpbuffer, sizeof(tmpbuffer));
287         for (va = VM_MIN_KERNEL_ADDRESS; va < kva_end; va += L2_SIZE) {
288                 if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) {
289                         /* We always write a page, even if it is zero */
290                         error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
291                         if (error)
292                                 goto fail;
293                         /* flush, in case we reuse tmpbuffer in the same block*/
294                         error = blk_flush(di);
295                         if (error)
296                                 goto fail;
297                         continue;
298                 }
299
300                 l1e = atomic_load_64(l1);
301                 l2e = atomic_load_64(l2);
302                 if ((l1e & ATTR_DESCR_MASK) == L1_BLOCK) {
303                         /*
304                          * Handle a 1GB block mapping: write out 512 fake L2
305                          * pages.
306                          */
307                         pa = PTE_TO_PHYS(l1e) | (va & L1_OFFSET);
308
309                         for (i = 0; i < Ln_ENTRIES; i++) {
310                                 for (j = 0; j < Ln_ENTRIES; j++) {
311                                         tmpbuffer[j] = (pa + i * L2_SIZE +
312                                             j * PAGE_SIZE) | ATTR_DEFAULT |
313                                             L3_PAGE;
314                                 }
315                                 error = blk_write(di, (char *)&tmpbuffer, 0,
316                                     PAGE_SIZE);
317                                 if (error)
318                                         goto fail;
319                         }
320                         /* flush, in case we reuse tmpbuffer in the same block*/
321                         error = blk_flush(di);
322                         if (error)
323                                 goto fail;
324                         bzero(&tmpbuffer, sizeof(tmpbuffer));
325                         va += L1_SIZE - L2_SIZE;
326                 } else if ((l2e & ATTR_DESCR_MASK) == L2_BLOCK) {
327                         pa = PTE_TO_PHYS(l2e) | (va & L2_OFFSET);
328
329                         /* Generate fake l3 entries based upon the l1 entry */
330                         for (i = 0; i < Ln_ENTRIES; i++) {
331                                 tmpbuffer[i] = (pa + i * PAGE_SIZE) |
332                                     ATTR_DEFAULT | L3_PAGE;
333                         }
334                         error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE);
335                         if (error)
336                                 goto fail;
337                         /* flush, in case we reuse fakepd in the same block */
338                         error = blk_flush(di);
339                         if (error)
340                                 goto fail;
341                         bzero(&tmpbuffer, sizeof(tmpbuffer));
342                         continue;
343                 } else {
344                         pa = PTE_TO_PHYS(l2e);
345
346                         /*
347                          * We always write a page, even if it is zero. If pa
348                          * is malformed, write the zeroed tmpbuffer.
349                          */
350                         if (PHYS_IN_DMAP(pa) && vm_phys_is_dumpable(pa))
351                                 error = blk_write(di, NULL, pa, PAGE_SIZE);
352                         else
353                                 error = blk_write(di, (char *)&tmpbuffer, 0,
354                                     PAGE_SIZE);
355                         if (error)
356                                 goto fail;
357                 }
358         }
359
360         /* Dump memory chunks */
361         VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) {
362                 error = blk_write(di, 0, pa, PAGE_SIZE);
363                 if (error)
364                         goto fail;
365         }
366
367         error = blk_flush(di);
368         if (error)
369                 goto fail;
370
371         error = dump_finish(di, &kdh);
372         if (error != 0)
373                 goto fail;
374
375         printf("\nDump complete\n");
376         return (0);
377
378 fail:
379         if (error < 0)
380                 error = -error;
381
382         printf("\n");
383         if (error == ENOSPC) {
384                 printf("Dump map grown while dumping. ");
385                 if (retry_count < 5) {
386                         printf("Retrying...\n");
387                         goto retry;
388                 }
389                 printf("Dump failed.\n");
390         }
391         else if (error == ECANCELED)
392                 printf("Dump aborted\n");
393         else if (error == E2BIG) {
394                 printf("Dump failed. Partition too small (about %lluMB were "
395                     "needed this time).\n", (long long)dumpsize >> 20);
396         } else
397                 printf("** DUMP FAILED (ERROR %d) **\n", error);
398         return (error);
399 }