196650Sobrien/*-
2190786Smarcel * Copyright (c) 2008, Juniper Networks, Inc.
396650Sobrien * All rights reserved.
496650Sobrien *
596650Sobrien * Redistribution and use in source and binary forms, with or without
696650Sobrien * modification, are permitted provided that the following conditions
796650Sobrien * are met:
896650Sobrien * 1. Redistributions of source code must retain the above copyright
996650Sobrien *    notice, this list of conditions and the following disclaimer.
1096650Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1196650Sobrien *    notice, this list of conditions and the following disclaimer in the
1296650Sobrien *    documentation and/or other materials provided with the distribution.
13190786Smarcel * 3. Neither the name of the author nor the names of any co-contributors
14190786Smarcel *    may be used to endorse or promote products derived from this software
15190786Smarcel *    without specific prior written permission.
1696650Sobrien *
17190786Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1896650Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1996650Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20190786Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21190786Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22190786Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23190786Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24190786Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25190786Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26190786Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2796650Sobrien */
2896650Sobrien
2996650Sobrien#include <sys/cdefs.h>
3096650Sobrien__FBSDID("$FreeBSD: releng/10.3/lib/libkvm/kvm_powerpc.c 217744 2011-01-23 11:08:28Z uqs $");
3196650Sobrien
3296650Sobrien#include <sys/param.h>
33190786Smarcel#include <sys/endian.h>
34190786Smarcel#include <sys/kerneldump.h>
35190786Smarcel#include <sys/mman.h>
3696650Sobrien
3796650Sobrien#include <vm/vm.h>
3896650Sobrien
3996650Sobrien#include <db.h>
40190786Smarcel#include <elf.h>
4196650Sobrien#include <limits.h>
4296650Sobrien#include <kvm.h>
4396650Sobrien#include <stdlib.h>
44217744Suqs#include <string.h>
4596650Sobrien
4696650Sobrien#include "kvm_private.h"
4796650Sobrien
48190786Smarcelstruct vmstate {
49190786Smarcel	void		*map;
50190786Smarcel	size_t		mapsz;
51190786Smarcel	size_t		dmphdrsz;
52190786Smarcel	Elf32_Ehdr	*eh;
53190786Smarcel	Elf32_Phdr	*ph;
54190786Smarcel};
55190786Smarcel
56190786Smarcelstatic int
57190786Smarcelvalid_elf_header(Elf32_Ehdr *eh)
5896650Sobrien{
59190786Smarcel
60190786Smarcel	if (!IS_ELF(*eh))
61190786Smarcel		return (0);
62190786Smarcel	if (eh->e_ident[EI_CLASS] != ELFCLASS32)
63190786Smarcel		return (0);
64190786Smarcel	if (eh->e_ident[EI_DATA] != ELFDATA2MSB)
65190786Smarcel		return (0);
66190786Smarcel	if (eh->e_ident[EI_VERSION] != EV_CURRENT)
67190786Smarcel		return (0);
68190786Smarcel	if (eh->e_ident[EI_OSABI] != ELFOSABI_STANDALONE)
69190786Smarcel		return (0);
70190786Smarcel	if (be16toh(eh->e_type) != ET_CORE)
71190786Smarcel		return (0);
72190786Smarcel	if (be16toh(eh->e_machine) != EM_PPC)
73190786Smarcel		return (0);
74190786Smarcel	/* Can't think of anything else to check... */
75190786Smarcel	return (1);
7696650Sobrien}
7796650Sobrien
78190786Smarcelstatic size_t
79190786Smarceldump_header_size(struct kerneldumpheader *dh)
8096650Sobrien{
81190786Smarcel
82190786Smarcel	if (strcmp(dh->magic, KERNELDUMPMAGIC) != 0)
83190786Smarcel		return (0);
84190786Smarcel	if (strcmp(dh->architecture, "powerpc") != 0)
85190786Smarcel		return (0);
86190786Smarcel	/* That should do it... */
87190786Smarcel	return (sizeof(*dh));
8896650Sobrien}
8996650Sobrien
90190786Smarcel/*
91190786Smarcel * Map the ELF headers into the process' address space. We do this in two
92190786Smarcel * steps: first the ELF header itself and using that information the whole
93190786Smarcel * set of headers.
94190786Smarcel */
95190786Smarcelstatic int
96190786Smarcelpowerpc_maphdrs(kvm_t *kd)
9796650Sobrien{
98190786Smarcel	struct vmstate *vm;
99190786Smarcel	size_t mapsz;
100190786Smarcel
101190786Smarcel	vm = kd->vmst;
102190786Smarcel
103190786Smarcel	vm->mapsz = PAGE_SIZE;
104190786Smarcel	vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
105190786Smarcel	if (vm->map == MAP_FAILED) {
106190786Smarcel		_kvm_err(kd, kd->program, "cannot map corefile");
107190786Smarcel		return (-1);
108190786Smarcel	}
109190786Smarcel	vm->dmphdrsz = 0;
110190786Smarcel	vm->eh = vm->map;
111190786Smarcel	if (!valid_elf_header(vm->eh)) {
112190786Smarcel		/*
113190786Smarcel		 * Hmmm, no ELF header. Maybe we still have a dump header.
114190786Smarcel		 * This is normal when the core file wasn't created by
115190786Smarcel		 * savecore(8), but instead was dumped over TFTP. We can
116190786Smarcel		 * easily skip the dump header...
117190786Smarcel		 */
118190786Smarcel		vm->dmphdrsz = dump_header_size(vm->map);
119190786Smarcel		if (vm->dmphdrsz == 0)
120190786Smarcel			goto inval;
121190786Smarcel		vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
122190786Smarcel		if (!valid_elf_header(vm->eh))
123190786Smarcel			goto inval;
124190786Smarcel	}
125190786Smarcel	mapsz = be16toh(vm->eh->e_phentsize) * be16toh(vm->eh->e_phnum) +
126190786Smarcel	    be32toh(vm->eh->e_phoff);
127190786Smarcel	munmap(vm->map, vm->mapsz);
128190786Smarcel
129190786Smarcel	/* Map all headers. */
130190786Smarcel	vm->mapsz = vm->dmphdrsz + mapsz;
131190786Smarcel	vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
132190786Smarcel	if (vm->map == MAP_FAILED) {
133190786Smarcel		_kvm_err(kd, kd->program, "cannot map corefle headers");
134190786Smarcel		return (-1);
135190786Smarcel	}
136190786Smarcel	vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
137190786Smarcel	vm->ph = (void *)((uintptr_t)vm->eh + be32toh(vm->eh->e_phoff));
138190786Smarcel	return (0);
139190786Smarcel
140190786Smarcel inval:
141190786Smarcel	munmap(vm->map, vm->mapsz);
142190786Smarcel	vm->map = MAP_FAILED;
143190786Smarcel	_kvm_err(kd, kd->program, "invalid corefile");
144190786Smarcel	return (-1);
14596650Sobrien}
14696650Sobrien
147190786Smarcel/*
148190786Smarcel * Determine the offset within the corefile corresponding the virtual
149190786Smarcel * address. Return the number of contiguous bytes in the corefile or
150190786Smarcel * 0 when the virtual address is invalid.
151190786Smarcel */
152190786Smarcelstatic size_t
153190786Smarcelpowerpc_va2off(kvm_t *kd, u_long va, off_t *ofs)
15496650Sobrien{
155190786Smarcel	struct vmstate *vm = kd->vmst;
156190786Smarcel	Elf32_Phdr *ph;
157190786Smarcel	int nph;
158190786Smarcel
159190786Smarcel	ph = vm->ph;
160190786Smarcel	nph = be16toh(vm->eh->e_phnum);
161190786Smarcel	while (nph && (va < be32toh(ph->p_vaddr) ||
162190786Smarcel	    va >= be32toh(ph->p_vaddr) + be32toh(ph->p_memsz))) {
163190786Smarcel		nph--;
164190786Smarcel		ph = (void *)((uintptr_t)ph + be16toh(vm->eh->e_phentsize));
165190786Smarcel	}
166190786Smarcel	if (nph == 0)
167190786Smarcel		return (0);
168190786Smarcel
169190786Smarcel	/* Segment found. Return file offset and range. */
170190786Smarcel	*ofs = vm->dmphdrsz + be32toh(ph->p_offset) +
171190786Smarcel	    (va - be32toh(ph->p_vaddr));
172190786Smarcel	return (be32toh(ph->p_memsz) - (va - be32toh(ph->p_vaddr)));
17396650Sobrien}
17496650Sobrien
175190786Smarcelvoid
176190786Smarcel_kvm_freevtop(kvm_t *kd)
177190786Smarcel{
178190786Smarcel	struct vmstate *vm = kd->vmst;
179190786Smarcel
180190786Smarcel	if (vm == NULL)
181190786Smarcel		return;
182190786Smarcel
183190786Smarcel	if (vm->eh != MAP_FAILED) {
184190786Smarcel		munmap(vm->eh, vm->mapsz);
185190786Smarcel		vm->eh = MAP_FAILED;
186190786Smarcel	}
187190786Smarcel	free(vm);
188190786Smarcel	kd->vmst = NULL;
189190786Smarcel}
190190786Smarcel
19196650Sobrienint
192190786Smarcel_kvm_initvtop(kvm_t *kd)
19396650Sobrien{
19496650Sobrien
195190786Smarcel	kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
196190786Smarcel	if (kd->vmst == NULL) {
197190786Smarcel		_kvm_err(kd, kd->program, "out of virtual memory");
198190786Smarcel		return (-1);
199190786Smarcel	}
200190786Smarcel	if (powerpc_maphdrs(kd) == -1) {
201190786Smarcel		free(kd->vmst);
202190786Smarcel		kd->vmst = NULL;
203190786Smarcel		return (-1);
204190786Smarcel	}
205190786Smarcel	return (0);
206190786Smarcel}
20796650Sobrien
208190786Smarcelint
209190786Smarcel_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
210190786Smarcel{
211190786Smarcel	struct vmstate *vm;
212190786Smarcel
213190786Smarcel	vm = kd->vmst;
214190786Smarcel	if (vm->ph->p_paddr == ~0U)
215190786Smarcel		return ((int)powerpc_va2off(kd, va, ofs));
216190786Smarcel
217190786Smarcel	_kvm_err(kd, kd->program, "Raw corefile not supported");
21896650Sobrien	return (0);
21996650Sobrien}
220