The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/arm/minidump_machdep.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
    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  * from: FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD: releng/8.1/sys/arm/arm/minidump_machdep.c 196019 2009-08-01 19:26:27Z rwatson $");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/conf.h>
   35 #include <sys/cons.h>
   36 #include <sys/kernel.h>
   37 #include <sys/kerneldump.h>
   38 #include <sys/msgbuf.h>
   39 #include <vm/vm.h>
   40 #include <vm/pmap.h>
   41 #include <machine/pmap.h>
   42 #include <machine/atomic.h>
   43 #include <machine/elf.h>
   44 #include <machine/md_var.h>
   45 #include <machine/vmparam.h>
   46 #include <machine/minidump.h>
   47 #include <machine/cpufunc.h>
   48 
   49 CTASSERT(sizeof(struct kerneldumpheader) == 512);
   50 
   51 /*
   52  * Don't touch the first SIZEOF_METADATA bytes on the dump device. This
   53  * is to protect us from metadata and to protect metadata from us.
   54  */
   55 #define SIZEOF_METADATA         (64*1024)
   56 
   57 uint32_t *vm_page_dump;
   58 int vm_page_dump_size;
   59 
   60 static struct kerneldumpheader kdh;
   61 static off_t dumplo;
   62 
   63 /* Handle chunked writes. */
   64 static size_t fragsz, offset;
   65 static void *dump_va;
   66 static uint64_t counter, progress;
   67 
   68 CTASSERT(sizeof(*vm_page_dump) == 4);
   69 
   70 static int
   71 is_dumpable(vm_paddr_t pa)
   72 {
   73         int i;
   74 
   75         for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
   76                 if (pa >= dump_avail[i] && pa < dump_avail[i + 1])
   77                         return (1);
   78         }
   79         return (0);
   80 }
   81 
   82 #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8)
   83 
   84 static int
   85 blk_flush(struct dumperinfo *di)
   86 {
   87         int error;
   88 
   89         if (fragsz == 0)
   90                 return (0);
   91 
   92         error = dump_write(di, (char*)dump_va + offset, 0, dumplo, fragsz - offset);
   93         dumplo += (fragsz - offset);
   94         fragsz = 0;
   95         offset = 0;
   96         return (error);
   97 }
   98 
   99 static int
  100 blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz)
  101 {
  102         size_t len;
  103         int error, i, c;
  104         u_int maxdumpsz;
  105 
  106         maxdumpsz = di->maxiosize;
  107 
  108         if (maxdumpsz == 0)     /* seatbelt */
  109                 maxdumpsz = PAGE_SIZE;
  110 
  111         error = 0;
  112 
  113         if (ptr != NULL && pa != 0) {
  114                 printf("cant have both va and pa!\n");
  115                 return (EINVAL);
  116         }
  117 
  118         if (ptr != NULL) {
  119                 /* If we're doing a virtual dump, flush any pre-existing pa pages */
  120                 error = blk_flush(di);
  121                 if (error)
  122                         return (error);
  123         }
  124 
  125         while (sz) {
  126                 if (fragsz == 0) {
  127                         offset = pa & PAGE_MASK;
  128                         fragsz += offset;
  129                 }
  130                 len = maxdumpsz - fragsz;
  131                 if (len > sz)
  132                         len = sz;
  133                 counter += len;
  134                 progress -= len;
  135 
  136                 if (counter >> 22) {
  137                         printf(" %lld", PG2MB(progress >> PAGE_SHIFT));
  138                         counter &= (1<<22) - 1;
  139                 }
  140 
  141                 if (ptr) {
  142                         error = dump_write(di, ptr, 0, dumplo, len);
  143                         if (error)
  144                                 return (error);
  145                         dumplo += len;
  146                         ptr += len;
  147                         sz -= len;
  148                 } else {
  149                         for (i = 0; i < len; i += PAGE_SIZE)
  150                                 dump_va = pmap_kenter_temp(pa + i,
  151                                     (i + fragsz) >> PAGE_SHIFT);
  152                         fragsz += len;
  153                         pa += len;
  154                         sz -= len;
  155                         if (fragsz == maxdumpsz) {
  156                                 error = blk_flush(di);
  157                                 if (error)
  158                                         return (error);
  159                         }
  160                 }
  161 
  162                 /* Check for user abort. */
  163                 c = cncheckc();
  164                 if (c == 0x03)
  165                         return (ECANCELED);
  166                 if (c != -1)
  167                         printf(" (CTRL-C to abort) ");
  168         }
  169 
  170         return (0);
  171 }
  172 
  173 static int
  174 blk_write_cont(struct dumperinfo *di, vm_paddr_t pa, size_t sz)
  175 {
  176         int error;
  177 
  178         error = blk_write(di, 0, pa, sz);
  179         if (error)
  180                 return (error);
  181 
  182         error = blk_flush(di);
  183         if (error)
  184                 return (error);
  185 
  186         return (0);
  187 }
  188 
  189 /* A fake page table page, to avoid having to handle both 4K and 2M pages */
  190 static pt_entry_t fakept[NPTEPG];
  191 
  192 void
  193 minidumpsys(struct dumperinfo *di)
  194 {
  195         struct minidumphdr mdhdr;
  196         uint64_t dumpsize;
  197         uint32_t ptesize;
  198         uint32_t bits;
  199         uint32_t pa, prev_pa = 0, count = 0;
  200         vm_offset_t va;
  201         pd_entry_t *pdp;
  202         pt_entry_t *pt, *ptp;
  203         int i, k, bit, error;
  204         char *addr;
  205 
  206         /* Flush cache */
  207         cpu_idcache_wbinv_all();
  208         cpu_l2cache_wbinv_all();
  209 
  210         counter = 0;
  211         /* Walk page table pages, set bits in vm_page_dump */
  212         ptesize = 0;
  213         for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
  214                 /*
  215                  * We always write a page, even if it is zero. Each
  216                  * page written corresponds to 2MB of space
  217                  */
  218                 ptesize += L2_TABLE_SIZE_REAL;
  219                 pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp);
  220                 if (pmap_pde_v(pdp) && pmap_pde_section(pdp)) {
  221                         /* This is a section mapping 1M page. */
  222                         pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK);
  223                         for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) {
  224                                 if (is_dumpable(pa))
  225                                         dump_add_page(pa);
  226                                 pa += PAGE_SIZE;
  227                         }
  228                         continue;
  229                 }
  230                 if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) {
  231                         /* Set bit for each valid page in this 1MB block */
  232                         addr = pmap_kenter_temp(*pdp & L1_C_ADDR_MASK, 0);
  233                         pt = (pt_entry_t*)(addr +
  234                             (((uint32_t)*pdp  & L1_C_ADDR_MASK) & PAGE_MASK));
  235                         for (k = 0; k < 256; k++) {
  236                                 if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_L) {
  237                                         pa = (pt[k] & L2_L_FRAME) |
  238                                             (va & L2_L_OFFSET);
  239                                         for (i = 0; i < 16; i++) {
  240                                                 if (is_dumpable(pa))
  241                                                         dump_add_page(pa);
  242                                                 k++;
  243                                                 pa += PAGE_SIZE;
  244                                         }
  245                                 } else if ((pt[k] & L2_TYPE_MASK) == L2_TYPE_S) {
  246                                         pa = (pt[k] & L2_S_FRAME) |
  247                                             (va & L2_S_OFFSET);
  248                                         if (is_dumpable(pa))
  249                                                 dump_add_page(pa);
  250                                 }
  251                         }
  252                 } else {
  253                         /* Nothing, we're going to dump a null page */
  254                 }
  255         }
  256 
  257         /* Calculate dump size. */
  258         dumpsize = ptesize;
  259         dumpsize += round_page(msgbufp->msg_size);
  260         dumpsize += round_page(vm_page_dump_size);
  261 
  262         for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
  263                 bits = vm_page_dump[i];
  264                 while (bits) {
  265                         bit = ffs(bits) - 1;
  266                         pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
  267                             bit) * PAGE_SIZE;
  268                         /* Clear out undumpable pages now if needed */
  269                         if (is_dumpable(pa))
  270                                 dumpsize += PAGE_SIZE;
  271                         else
  272                                 dump_drop_page(pa);
  273                         bits &= ~(1ul << bit);
  274                 }
  275         }
  276 
  277         dumpsize += PAGE_SIZE;
  278 
  279         /* Determine dump offset on device. */
  280         if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
  281                 error = ENOSPC;
  282                 goto fail;
  283         }
  284 
  285         dumplo = di->mediaoffset + di->mediasize - dumpsize;
  286         dumplo -= sizeof(kdh) * 2;
  287         progress = dumpsize;
  288 
  289         /* Initialize mdhdr */
  290         bzero(&mdhdr, sizeof(mdhdr));
  291         strcpy(mdhdr.magic, MINIDUMP_MAGIC);
  292         mdhdr.version = MINIDUMP_VERSION;
  293         mdhdr.msgbufsize = msgbufp->msg_size;
  294         mdhdr.bitmapsize = vm_page_dump_size;
  295         mdhdr.ptesize = ptesize;
  296         mdhdr.kernbase = KERNBASE;
  297 
  298         mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize,
  299             di->blocksize);
  300 
  301         printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576);
  302         printf("Dumping %llu MB:", (long long)dumpsize >> 20);
  303 
  304         /* Dump leader */
  305         error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
  306         if (error)
  307                 goto fail;
  308         dumplo += sizeof(kdh);
  309 
  310         /* Dump my header */
  311         bzero(&fakept, sizeof(fakept));
  312         bcopy(&mdhdr, &fakept, sizeof(mdhdr));
  313         error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE);
  314         if (error)
  315                 goto fail;
  316 
  317         /* Dump msgbuf up front */
  318         error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size));
  319         if (error)
  320                 goto fail;
  321 
  322         /* Dump bitmap */
  323         error = blk_write(di, (char *)vm_page_dump, 0,
  324             round_page(vm_page_dump_size));
  325         if (error)
  326                 goto fail;
  327 
  328         /* Dump kernel page table pages */
  329         for (va = KERNBASE; va < kernel_vm_end; va += NBPDR) {
  330                 /* We always write a page, even if it is zero */
  331                 pmap_get_pde_pte(pmap_kernel(), va, &pdp, &ptp);
  332 
  333                 if (pmap_pde_v(pdp) && pmap_pde_section(pdp))  {
  334                         if (count) {
  335                                 error = blk_write_cont(di, prev_pa,
  336                                     count * L2_TABLE_SIZE_REAL);
  337                                 if (error)
  338                                         goto fail;
  339                                 count = 0;
  340                                 prev_pa = 0;
  341                         }
  342                         /* This is a single 2M block. Generate a fake PTP */
  343                         pa = (*pdp & L1_S_ADDR_MASK) | (va & ~L1_S_ADDR_MASK);
  344                         for (k = 0; k < (L1_S_SIZE / PAGE_SIZE); k++) {
  345                                 fakept[k] = L2_S_PROTO | (pa + (k * PAGE_SIZE)) |
  346                                     L2_S_PROT(PTE_KERNEL,
  347                                     VM_PROT_READ | VM_PROT_WRITE);
  348                         }
  349                         error = blk_write(di, (char *)&fakept, 0,
  350                             L2_TABLE_SIZE_REAL);
  351                         if (error)
  352                                 goto fail;
  353                         /* Flush, in case we reuse fakept in the same block */
  354                         error = blk_flush(di);
  355                         if (error)
  356                                 goto fail;
  357                         continue;
  358                 }
  359                 if (pmap_pde_v(pdp) && pmap_pde_page(pdp)) {
  360                         pa = *pdp & L1_C_ADDR_MASK;
  361                         if (!count) {
  362                                 prev_pa = pa;
  363                                 count++;
  364                         }
  365                         else {
  366                                 if (pa == (prev_pa + count * L2_TABLE_SIZE_REAL))
  367                                         count++;
  368                                 else {
  369                                         error = blk_write_cont(di, prev_pa,
  370                                             count * L2_TABLE_SIZE_REAL);
  371                                         if (error)
  372                                                 goto fail;
  373                                         count = 1;
  374                                         prev_pa = pa;
  375                                 }
  376                         }
  377                 } else {
  378                         if (count) {
  379                                 error = blk_write_cont(di, prev_pa,
  380                                     count * L2_TABLE_SIZE_REAL);
  381                                 if (error)
  382                                         goto fail;
  383                                 count = 0;
  384                                 prev_pa = 0;
  385                         }
  386                         bzero(fakept, sizeof(fakept));
  387                         error = blk_write(di, (char *)&fakept, 0,
  388                             L2_TABLE_SIZE_REAL);
  389                         if (error)
  390                                 goto fail;
  391                         /* Flush, in case we reuse fakept in the same block */
  392                         error = blk_flush(di);
  393                         if (error)
  394                                 goto fail;
  395                 }
  396         }
  397 
  398         if (count) {
  399                 error = blk_write_cont(di, prev_pa, count * L2_TABLE_SIZE_REAL);
  400                 if (error)
  401                         goto fail;
  402                 count = 0;
  403                 prev_pa = 0;
  404         }
  405 
  406         /* Dump memory chunks */
  407         for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) {
  408                 bits = vm_page_dump[i];
  409                 while (bits) {
  410                         bit = ffs(bits) - 1;
  411                         pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) +
  412                             bit) * PAGE_SIZE;
  413                         if (!count) {
  414                                 prev_pa = pa;
  415                                 count++;
  416                         } else {
  417                                 if (pa == (prev_pa + count * PAGE_SIZE))
  418                                         count++;
  419                                 else {
  420                                         error = blk_write_cont(di, prev_pa,
  421                                             count * PAGE_SIZE);
  422                                         if (error)
  423                                                 goto fail;
  424                                         count = 1;
  425                                         prev_pa = pa;
  426                                 }
  427                         }
  428                         bits &= ~(1ul << bit);
  429                 }
  430         }
  431         if (count) {
  432                 error = blk_write_cont(di, prev_pa, count * PAGE_SIZE);
  433                 if (error)
  434                         goto fail;
  435                 count = 0;
  436                 prev_pa = 0;
  437         }
  438 
  439         /* Dump trailer */
  440         error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
  441         if (error)
  442                 goto fail;
  443         dumplo += sizeof(kdh);
  444 
  445         /* Signal completion, signoff and exit stage left. */
  446         dump_write(di, NULL, 0, 0, 0);
  447         printf("\nDump complete\n");
  448         return;
  449 
  450 fail:
  451         if (error < 0)
  452                 error = -error;
  453 
  454         if (error == ECANCELED)
  455                 printf("\nDump aborted\n");
  456         else if (error == ENOSPC)
  457                 printf("\nDump failed. Partition too small.\n");
  458         else
  459                 printf("\n** DUMP FAILED (ERROR %d) **\n", error);
  460 }
  461 
  462 void
  463 dump_add_page(vm_paddr_t pa)
  464 {
  465         int idx, bit;
  466 
  467         pa >>= PAGE_SHIFT;
  468         idx = pa >> 5;          /* 2^5 = 32 */
  469         bit = pa & 31;
  470         atomic_set_int(&vm_page_dump[idx], 1ul << bit);
  471 }
  472 
  473 void
  474 dump_drop_page(vm_paddr_t pa)
  475 {
  476         int idx, bit;
  477 
  478         pa >>= PAGE_SHIFT;
  479         idx = pa >> 5;          /* 2^5 = 32 */
  480         bit = pa & 31;
  481         atomic_clear_int(&vm_page_dump[idx], 1ul << bit);
  482 }

Cache object: e380f931c3f13ff26ff350d0d5ffd9ad


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.