kvm_powerpc64.c revision 217777
12061Sjkh/*-
236682Sbde * Copyright (c) 2008, Juniper Networks, Inc.
32061Sjkh * All rights reserved.
433611Sjb *
532427Sjb * Redistribution and use in source and binary forms, with or without
632427Sjb * modification, are permitted provided that the following conditions
736111Sjb * are met:
833611Sjb * 1. Redistributions of source code must retain the above copyright
932427Sjb *    notice, this list of conditions and the following disclaimer.
1032427Sjb * 2. Redistributions in binary form must reproduce the above copyright
112061Sjkh *    notice, this list of conditions and the following disclaimer in the
1215603Smarkm *    documentation and/or other materials provided with the distribution.
1330169Sjkh * 3. Neither the name of the author nor the names of any co-contributors
1420710Sasami *    may be used to endorse or promote products derived from this software
1520710Sasami *    without specific prior written permission.
163197Scsgr *
172061Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1812483Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1934509Sbde * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
202160Scsgr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
212834Swollman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
222061Sjkh * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232061Sjkh * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242160Scsgr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2517308Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2619320Sadam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2727788Sasami */
2830169Sjkh
2925980Sasami#include <sys/cdefs.h>
301594Srgrimes__FBSDID("$FreeBSD: head/lib/libkvm/kvm_powerpc64.c 217777 2011-01-24 11:06:40Z uqs $");
3117308Speter
3217308Speter#include <sys/param.h>
3327910Sasami#include <sys/endian.h>
3427910Sasami#include <sys/kerneldump.h>
3527910Sasami#include <sys/mman.h>
3617308Speter
3717308Speter#include <vm/vm.h>
3817308Speter
3919175Sbde#include <db.h>
4019175Sbde#include <elf.h>
4119175Sbde#include <limits.h>
4219175Sbde#include <kvm.h>
4317308Speter#include <stdlib.h>
4427910Sasami#include <string.h>
4534509Sbde
4627910Sasami#include "kvm_private.h"
4717308Speter
482061Sjkhstruct vmstate {
492061Sjkh	void		*map;
501594Srgrimes	size_t		mapsz;
5130169Sjkh	size_t		dmphdrsz;
5230169Sjkh	Elf64_Ehdr	*eh;
5330169Sjkh	Elf64_Phdr	*ph;
5430169Sjkh};
5530169Sjkh
5630169Sjkhstatic int
5730169Sjkhvalid_elf_header(Elf64_Ehdr *eh)
5830169Sjkh{
597407Srgrimes
607108Sphk	if (!IS_ELF(*eh))
617108Sphk		return (0);
627108Sphk	if (eh->e_ident[EI_CLASS] != ELFCLASS64)
637407Srgrimes		return (0);
647407Srgrimes	if (eh->e_ident[EI_DATA] != ELFDATA2MSB)
657407Srgrimes		return (0);
667108Sphk	if (eh->e_ident[EI_VERSION] != EV_CURRENT)
672061Sjkh		return (0);
682061Sjkh	if (eh->e_ident[EI_OSABI] != ELFOSABI_STANDALONE)
692061Sjkh		return (0);
7017308Speter	if (be16toh(eh->e_type) != ET_CORE)
712061Sjkh		return (0);
722061Sjkh	if (be16toh(eh->e_machine) != EM_PPC64)
732061Sjkh		return (0);
742061Sjkh	/* Can't think of anything else to check... */
752061Sjkh	return (1);
7635427Sbde}
7735427Sbde
7830169Sjkhstatic size_t
792626Scsgrdump_header_size(struct kerneldumpheader *dh)
802061Sjkh{
812061Sjkh
822061Sjkh	if (strcmp(dh->magic, KERNELDUMPMAGIC) != 0)
832061Sjkh		return (0);
842061Sjkh	if (strcmp(dh->architecture, "powerpc64") != 0)
852061Sjkh		return (0);
8619320Sadam	/* That should do it... */
872061Sjkh	return (sizeof(*dh));
882061Sjkh}
892061Sjkh
902061Sjkh/*
912061Sjkh * Map the ELF headers into the process' address space. We do this in two
922061Sjkh * steps: first the ELF header itself and using that information the whole
932061Sjkh * set of headers.
942061Sjkh */
952061Sjkhstatic int
962061Sjkhpowerpc_maphdrs(kvm_t *kd)
972061Sjkh{
982834Swollman	struct vmstate *vm;
992834Swollman	size_t mapsz;
1002834Swollman
1012834Swollman	vm = kd->vmst;
1022834Swollman
1032834Swollman	vm->mapsz = PAGE_SIZE;
1041594Srgrimes	vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
1054486Sphk	if (vm->map == MAP_FAILED) {
1064486Sphk		_kvm_err(kd, kd->program, "cannot map corefile");
1074486Sphk		return (-1);
1084486Sphk	}
1094486Sphk	vm->dmphdrsz = 0;
1102061Sjkh	vm->eh = vm->map;
1112061Sjkh	if (!valid_elf_header(vm->eh)) {
11225979Sjkh		/*
11325979Sjkh		 * Hmmm, no ELF header. Maybe we still have a dump header.
11425979Sjkh		 * This is normal when the core file wasn't created by
11525979Sjkh		 * savecore(8), but instead was dumped over TFTP. We can
1162061Sjkh		 * easily skip the dump header...
11725979Sjkh		 */
1182061Sjkh		vm->dmphdrsz = dump_header_size(vm->map);
1192061Sjkh		if (vm->dmphdrsz == 0)
12017308Speter			goto inval;
1212061Sjkh		vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
1222061Sjkh		if (!valid_elf_header(vm->eh))
1232061Sjkh			goto inval;
1242061Sjkh	}
1252061Sjkh	mapsz = be16toh(vm->eh->e_phentsize) * be16toh(vm->eh->e_phnum) +
12612483Speter	    be64toh(vm->eh->e_phoff);
12712483Speter	munmap(vm->map, vm->mapsz);
12812483Speter
12912483Speter	/* Map all headers. */
1302061Sjkh	vm->mapsz = vm->dmphdrsz + mapsz;
13135479Sbde	vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
1328854Srgrimes	if (vm->map == MAP_FAILED) {
1332061Sjkh		_kvm_err(kd, kd->program, "cannot map corefle headers");
1342061Sjkh		return (-1);
13512483Speter	}
1362061Sjkh	vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
13735479Sbde	vm->ph = (void *)((uintptr_t)vm->eh + be64toh(vm->eh->e_phoff));
13835479Sbde	return (0);
13935479Sbde
14035479Sbde inval:
14135479Sbde	munmap(vm->map, vm->mapsz);
14235479Sbde	vm->map = MAP_FAILED;
14335479Sbde	_kvm_err(kd, kd->program, "invalid corefile");
14435479Sbde	return (-1);
14535479Sbde}
14635462Sjkh
14735462Sjkh/*
14818714Sache * Determine the offset within the corefile corresponding the virtual
14917308Speter * address. Return the number of contiguous bytes in the corefile or
15034541Sbde * 0 when the virtual address is invalid.
15134575Sbde */
15234575Sbdestatic size_t
15334575Sbdepowerpc64_va2off(kvm_t *kd, u_long va, off_t *ofs)
15434592Sbde{
15517308Speter	struct vmstate *vm = kd->vmst;
15634575Sbde	Elf64_Phdr *ph;
15735427Sbde	int nph;
15834575Sbde
15935427Sbde	ph = vm->ph;
16034575Sbde	nph = be16toh(vm->eh->e_phnum);
16115603Smarkm	while (nph && (va < be64toh(ph->p_vaddr) ||
16217308Speter	    va >= be64toh(ph->p_vaddr) + be64toh(ph->p_memsz))) {
16317308Speter		nph--;
16417308Speter		ph = (void *)((uintptr_t)ph + be16toh(vm->eh->e_phentsize));
16517308Speter	}
16617308Speter	if (nph == 0)
16717308Speter		return (0);
16817308Speter
16917308Speter	/* Segment found. Return file offset and range. */
17017308Speter	*ofs = vm->dmphdrsz + be64toh(ph->p_offset) +
17118362Sjkh	    (va - be64toh(ph->p_vaddr));
17219966Sache	return (be64toh(ph->p_memsz) - (va - be64toh(ph->p_vaddr)));
17318362Sjkh}
17417308Speter
17527910Sasamivoid
17617308Speter_kvm_freevtop(kvm_t *kd)
17717308Speter{
17817308Speter	struct vmstate *vm = kd->vmst;
17936074Sbde
18027910Sasami	if (vm == NULL)
18136074Sbde		return;
18236074Sbde
18327910Sasami	if (vm->eh != MAP_FAILED) {
18417308Speter		munmap(vm->eh, vm->mapsz);
1852061Sjkh		vm->eh = MAP_FAILED;
18627910Sasami	}
1872061Sjkh	free(vm);
18836074Sbde	kd->vmst = NULL;
18927910Sasami}
1902061Sjkh
19117308Speterint
19227910Sasami_kvm_initvtop(kvm_t *kd)
19317308Speter{
19427910Sasami
19527910Sasami	kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
19627910Sasami	if (kd->vmst == NULL) {
19717308Speter		_kvm_err(kd, kd->program, "out of virtual memory");
19827910Sasami		return (-1);
19917308Speter	}
20027910Sasami	if (powerpc_maphdrs(kd) == -1) {
20127910Sasami		free(kd->vmst);
20227910Sasami		kd->vmst = NULL;
20327910Sasami		return (-1);
20436622Scharnier	}
20536622Scharnier	return (0);
20627910Sasami}
20727910Sasami
20827910Sasamiint
20927910Sasami_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
21027910Sasami{
21127910Sasami	struct vmstate *vm;
21234509Sbde
21327910Sasami	vm = kd->vmst;
21427910Sasami	if (vm->ph->p_paddr == ~0UL)
21527910Sasami		return ((int)powerpc64_va2off(kd, va, ofs));
21636423Speter
21736423Speter	_kvm_err(kd, kd->program, "Raw corefile not supported");
21827910Sasami	return (0);
21936423Speter}
22035479Sbde