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:
parent
e01ad62b66
commit
c93dd30159
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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_ */
|
Loading…
Reference in New Issue