Add support for kcore headers to arm32 kernel core dumps.

The kcore code is based on i386's kcore header handling.

Having an asm stub for dumpsys, to dump the registers onto the stack, and
then call the C code to do the memory dump is based on amd64's core dump
code.

This allows a successful core dump on cats.

Part of fixing PR cats/18026.
This commit is contained in:
chris 2008-01-01 14:06:42 +00:00
parent e01ad62b66
commit c93dd30159
7 changed files with 250 additions and 46 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: locore.S,v 1.18 2005/12/11 12:16:41 christos Exp $ */
/* $NetBSD: locore.S,v 1.19 2008/01/01 14:06:42 chris Exp $ */
/*
* Copyright (C) 1994-1997 Mark Brinicombe
@ -212,5 +212,38 @@ _C_LABEL(esym): .word _C_LABEL(end)
ENTRY_NP(abort)
b _C_LABEL(abort)
/*
* part of doing a system dump, we need to save registers and cpsr onto the
* stack, then save the rest of the registers into the dumppcb
*/
ENTRY(dumpsys)
/* push registers onto stack */
stmfd sp!, {r0-r7, lr}
/* push the status bits onto the stack */
mrs r0, cpsr_all
stmfd sp!, {r0}
/* fill in dumppcb */
ldr r0, .Ldumppcb
#ifndef __XSCALE__
add r2, r0, #(PCB_R8)
stmia r2, {r8-r13}
#else
strd r8, [r0, #(PCB_R8)]
strd r10, [r0, #(PCB_R10)]
strd r12, [r0, #(PCB_R12)]
#endif
bl _C_LABEL(dodumpsys)
/* unwind the stack */
ldmfd sp!, {r0}
nop
ldmfd sp!, {r0-r7, pc}
.Ldumppcb:
.word _C_LABEL(dumppcb)
/* End of locore.S */

View File

@ -1,4 +1,4 @@
/* $NetBSD: mem.c,v 1.19 2007/03/04 10:21:26 christos Exp $ */
/* $NetBSD: mem.c,v 1.20 2008/01/01 14:06:42 chris Exp $ */
/*
* Copyright (c) 1982, 1986, 1990, 1993
@ -75,7 +75,7 @@
#include "opt_compat_netbsd.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mem.c,v 1.19 2007/03/04 10:21:26 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: mem.c,v 1.20 2008/01/01 14:06:42 chris Exp $");
#include <sys/param.h>
#include <sys/conf.h>
@ -91,7 +91,7 @@ __KERNEL_RCSID(0, "$NetBSD: mem.c,v 1.19 2007/03/04 10:21:26 christos Exp $");
#include <uvm/uvm_extern.h>
extern char *memhook; /* poor name! */
extern vaddr_t memhook; /* poor name! */
void *zeropage;
int physlock;
@ -143,14 +143,14 @@ mmrw(dev, uio, flags)
v = uio->uio_offset;
prot = uio->uio_rw == UIO_READ ? VM_PROT_READ :
VM_PROT_WRITE;
pmap_enter(pmap_kernel(), (vaddr_t)memhook,
pmap_enter(pmap_kernel(), memhook,
trunc_page(v), prot, prot|PMAP_WIRED);
pmap_update(pmap_kernel());
o = uio->uio_offset & PGOFSET;
c = min(uio->uio_resid, (int)(PAGE_SIZE - o));
error = uiomove((char *)memhook + o, c, uio);
pmap_remove(pmap_kernel(), (vaddr_t)memhook,
(vaddr_t)memhook + PAGE_SIZE);
pmap_remove(pmap_kernel(), memhook,
memhook + PAGE_SIZE);
pmap_update(pmap_kernel());
break;

View File

@ -1,4 +1,4 @@
/* $NetBSD: pmap.c,v 1.169 2007/11/08 11:10:28 matt Exp $ */
/* $NetBSD: pmap.c,v 1.170 2008/01/01 14:06:42 chris Exp $ */
/*
* Copyright 2003 Wasabi Systems, Inc.
@ -212,7 +212,7 @@
#include <machine/param.h>
#include <arm/arm32/katelib.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.169 2007/11/08 11:10:28 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.170 2008/01/01 14:06:42 chris Exp $");
#ifdef PMAP_DEBUG
@ -303,7 +303,7 @@ static paddr_t pmap_kernel_l2ptp_phys;
*/
static pt_entry_t *csrc_pte, *cdst_pte;
static vaddr_t csrcp, cdstp;
char *memhook;
vaddr_t memhook;
extern void *msgbufaddr;
/*
@ -3963,7 +3963,7 @@ pmap_bootstrap(pd_entry_t *kernel_l1pt, vaddr_t vstart, vaddr_t vend)
pmap_set_pt_cache_mode(kernel_l1pt, (vaddr_t)csrc_pte);
pmap_alloc_specials(&virtual_avail, 1, &cdstp, &cdst_pte);
pmap_set_pt_cache_mode(kernel_l1pt, (vaddr_t)cdst_pte);
pmap_alloc_specials(&virtual_avail, 1, (void *)&memhook, NULL);
pmap_alloc_specials(&virtual_avail, 1, &memhook, NULL);
pmap_alloc_specials(&virtual_avail, round_page(MSGBUFSIZE) / PAGE_SIZE,
(void *)&msgbufaddr, NULL);
@ -4985,6 +4985,14 @@ pmap_uarea(vaddr_t va)
}
#endif /* ARM_MMU_XSCALE == 1 */
/*
* return the PA of the current L1 table, for use when handling a crash dump
*/
uint32_t pmap_kernel_L1_addr()
{
return pmap_kernel()->pm_l1->l1_physaddr;
}
#if defined(DDB)
/*
* A couple of ddb-callable functions for dumping pmaps

View File

@ -1,4 +1,4 @@
/* $NetBSD: stubs.c,v 1.17 2007/03/04 05:59:37 christos Exp $ */
/* $NetBSD: stubs.c,v 1.18 2008/01/01 14:06:42 chris Exp $ */
/*
* Copyright (c) 1994-1998 Mark Brinicombe.
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: stubs.c,v 1.17 2007/03/04 05:59:37 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: stubs.c,v 1.18 2008/01/01 14:06:42 chris Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -55,9 +55,17 @@ __KERNEL_RCSID(0, "$NetBSD: stubs.c,v 1.17 2007/03/04 05:59:37 christos Exp $");
#include <machine/bootconfig.h>
#include <machine/pcb.h>
#include <arm/arm32/machdep.h>
#include <arm/kcore.h>
#include <sys/kcore.h>
#include <sys/core.h>
#include <sys/exec_aout.h>
extern dev_t dumpdev;
int cpu_dump(void);
int cpu_dumpsize(void);
u_long cpu_dump_mempagecnt(void);
/*
* These variables are needed by /sbin/savecore
*/
@ -69,7 +77,7 @@ struct pcb dumppcb;
/*
* This is called by main to set dumplo and dumpsize.
* Dumps always skip the first CLBYTES of disk space
* Dumps always skip the first PAGE_SIZE of disk space
* in case there might be a disk label stored there.
* If there is extra space, put dump at the end to
* reduce the chance that swapping trashes it.
@ -79,46 +87,136 @@ void
cpu_dumpconf()
{
const struct bdevsw *bdev;
int nblks; /* size of dump area */
int nblks, dumpblks; /* size of dump area */
if (dumpdev == NODEV)
return;
bdev = bdevsw_lookup(dumpdev);
if (bdev == NULL) {
dumpdev = NODEV;
return;
}
if (bdev == NULL)
panic("dumpconf: bad dumpdev=0x%x", dumpdev);
if (bdev->d_psize == NULL)
return;
nblks = (*bdev->d_psize)(dumpdev);
if (nblks <= ctod(1))
return;
dumpsize = physmem;
dumpblks = cpu_dumpsize();
if (dumpblks < 0)
goto bad;
dumpblks += ctod(cpu_dump_mempagecnt());
/* Always skip the first CLBYTES, in case there is a label there. */
if (dumplo < ctod(1))
dumplo = ctod(1);
/* If dump won't fit (incl. room for possible label), punt. */
if (dumpblks > (nblks - ctod(1)))
goto bad;
/* Put dump at end of partition, and make it fit. */
if (dumpsize > dtoc(nblks - dumplo))
dumpsize = dtoc(nblks - dumplo);
if (dumplo < nblks - ctod(dumpsize))
dumplo = nblks - ctod(dumpsize);
/* Put dump at end of partition */
dumplo = nblks - dumpblks;
/* dumpsize is in page units, and doesn't include headers. */
dumpsize = cpu_dump_mempagecnt();
return;
bad:
dumpsize = 0;
}
/*
* cpu_dump: dump the machine-dependent kernel core dump headers.
*/
int
cpu_dump()
{
int (*dump)(dev_t, daddr_t, void *, size_t);
char bf[dbtob(1)];
kcore_seg_t *segp;
cpu_kcore_hdr_t *cpuhdrp;
phys_ram_seg_t *memsegp;
const struct bdevsw *bdev;
int i;
bdev = bdevsw_lookup(dumpdev);
if (bdev == NULL)
return (ENXIO);
dump = bdev->d_dump;
memset(bf, 0, sizeof bf);
segp = (kcore_seg_t *)bf;
cpuhdrp = (cpu_kcore_hdr_t *)&bf[ALIGN(sizeof(*segp))];
memsegp = (phys_ram_seg_t *)&bf[ ALIGN(sizeof(*segp)) +
ALIGN(sizeof(*cpuhdrp))];
/*
* Generate a segment header.
*/
CORE_SETMAGIC(*segp, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
segp->c_size = dbtob(1) - ALIGN(sizeof(*segp));
/*
* Add the machine-dependent header info.
*/
cpuhdrp->version = 1;
cpuhdrp->PAKernelL1Table = pmap_kernel_L1_addr();
cpuhdrp->UserL1TableSize = 0;
cpuhdrp->nmemsegs = bootconfig.dramblocks;
cpuhdrp->omemsegs = ALIGN(sizeof(*cpuhdrp));
/*
* Fill in the memory segment descriptors.
*/
for (i = 0; i < bootconfig.dramblocks; i++) {
memsegp[i].start = bootconfig.dram[i].address;
memsegp[i].size = bootconfig.dram[i].pages * PAGE_SIZE;
}
return (dump(dumpdev, dumplo, bf, dbtob(1)));
}
/*
* cpu_dumpsize: calculate size of machine-dependent kernel core dump headers.
*/
int
cpu_dumpsize()
{
int size;
size = ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t)) +
ALIGN( bootconfig.dramblocks * sizeof(phys_ram_seg_t));
if (roundup(size, dbtob(1)) != dbtob(1))
return (-1);
return (1);
}
/*
* cpu_dump_mempagecnt: calculate the size of RAM (in pages) to be dumped.
*/
u_long
cpu_dump_mempagecnt()
{
u_long i, n;
n = 0;
for (i = 0; i < bootconfig.dramblocks; i++) {
n += bootconfig.dram[i].pages;
}
return (n);
}
/* This should be moved to machdep.c */
extern char *memhook; /* XXX */
extern vaddr_t memhook; /* XXX */
/*
* Doadump comes here after turning off memory management and
* getting on the dump stack, either when called above, or by
* the auto-restart code.
*/
void dodumpsys(void);
void
dumpsys()
dodumpsys()
{
const struct bdevsw *bdev;
daddr_t blkno;
@ -129,8 +227,6 @@ dumpsys()
int len;
vaddr_t dumpspace;
/* Save registers. */
savectx(&dumppcb);
/* flush everything out of caches */
cpu_dcache_wbinv_all();
@ -138,19 +234,16 @@ dumpsys()
return;
if (dumpsize == 0) {
cpu_dumpconf();
if (dumpsize == 0)
return;
}
if (dumplo <= 0) {
if (dumplo <= 0 || dumpsize == 0) {
printf("\ndump to dev %u,%u not possible\n", major(dumpdev),
minor(dumpdev));
delay(5000000);
return;
}
printf("\ndumping to dev %u,%u offset %ld\n", major(dumpdev),
minor(dumpdev), dumplo);
blkno = dumplo;
dumpspace = (vaddr_t) memhook;
bdev = bdevsw_lookup(dumpdev);
if (bdev == NULL || bdev->d_psize == NULL)
@ -162,6 +255,11 @@ dumpsys()
return;
}
if ((error = cpu_dump()) != 0)
goto err;
blkno = dumplo + cpu_dumpsize();
dumpspace = memhook;
error = 0;
len = 0;
@ -172,19 +270,18 @@ dumpsys()
addr += PAGE_SIZE) {
if ((len % (1024*1024)) == 0)
printf("%d ", len / (1024*1024));
pmap_kenter_pa(dumpspace, addr, VM_PROT_READ);
pmap_update(pmap_kernel());
error = (*bdev->d_dump)(dumpdev,
blkno, (void *) dumpspace, PAGE_SIZE);
pmap_kremove(dumpspace, PAGE_SIZE);
pmap_update(pmap_kernel());
if (error) break;
if (error) goto err;
blkno += btodb(PAGE_SIZE);
len += PAGE_SIZE;
}
}
err:
switch (error) {
case ENXIO:
printf("device bad\n");
@ -206,12 +303,16 @@ dumpsys()
printf("aborted from console\n");
break;
default:
case 0:
printf("succeeded\n");
break;
default:
printf("error %d\n", error);
break;
}
printf("\n\n");
delay(1000000);
delay(5000000);
}
/* End of stubs.c */

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.34 2007/02/18 15:53:55 briggs Exp $
# $NetBSD: Makefile,v 1.35 2008/01/01 14:06:43 chris Exp $
INCSDIR= /usr/include/arm
@ -11,6 +11,7 @@ INCS= ansi.h aout_machdep.h armreg.h asm.h atomic.h \
ieee.h ieeefp.h \
int_const.h int_fmtio.h int_limits.h int_mwgwtypes.h int_types.h \
ipkdb.h \
kcore.h \
limits.h lock.h \
math.h mcontext.h mutex.h \
param.h pcb.h pmc.h proc.h profile.h rwlock.h \

View File

@ -1,4 +1,4 @@
/* $NetBSD: pmap.h,v 1.83 2007/10/17 19:53:41 garbled Exp $ */
/* $NetBSD: pmap.h,v 1.84 2008/01/01 14:06:43 chris Exp $ */
/*
* Copyright (c) 2002, 2003 Wasabi Systems, Inc.
@ -291,6 +291,10 @@ void pmap_devmap_register(const struct pmap_devmap *);
bool pmap_pageidlezero(paddr_t);
#define PMAP_PAGEIDLEZERO(pa) pmap_pageidlezero((pa))
/*
* used by dumpsys to record the PA of the L1 table
*/
uint32_t pmap_kernel_L1_addr(void);
/*
* The current top of kernel VM
*/

View File

@ -0,0 +1,57 @@
/* $NetBSD: kcore.h,v 1.1 2008/01/01 14:06:43 chris Exp $ */
/*
* Copyright (c) 1996 Carnegie-Mellon University.
* All rights reserved.
*
* Author: Chris G. Demetriou
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* Modified for NetBSD/i386 by Jason R. Thorpe, Numerical Aerospace
* Simulation Facility, NASA Ames Research Center.
*/
#ifndef _ARM_KCORE_H_
#define _ARM_KCORE_H_
typedef struct cpu_kcore_hdr {
uint32_t version; /* structure version */
uint32_t flags; /* flags */
#define KCORE_ARM_APX 0x0001 /* L1 tables are in APX
format */
uint32_t PAKernelL1Table; /* PA of kernel L1 table */
uint32_t PAUserL1Table; /* PA of userland L1 table */
uint16_t UserL1TableSize; /* size of User L1 table */
uint32_t nmemsegs; /* Number of RAM segments */
uint32_t omemsegs; /* offset to memsegs */
/*
* future versions will add fields here.
*/
#if 0
phys_ram_seg_t memsegs[]; /* RAM segments */
#endif
} cpu_kcore_hdr_t;
#endif /* _ARM_KCORE_H_ */