1184728Sraj/*-
2184728Sraj * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
3184728Sraj * Copyright (c) 2006 Peter Wemm
4184728Sraj *
5184728Sraj * Redistribution and use in source and binary forms, with or without
6184728Sraj * modification, are permitted provided that the following conditions
7184728Sraj * are met:
8184728Sraj * 1. Redistributions of source code must retain the above copyright
9184728Sraj *    notice, this list of conditions and the following disclaimer.
10184728Sraj * 2. Redistributions in binary form must reproduce the above copyright
11184728Sraj *    notice, this list of conditions and the following disclaimer in the
12184728Sraj *    documentation and/or other materials provided with the distribution.
13184728Sraj *
14184728Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184728Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184728Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184728Sraj * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184728Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184728Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184728Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184728Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184728Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184728Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184728Sraj * SUCH DAMAGE.
25184728Sraj *
26184728Sraj * From: FreeBSD: src/lib/libkvm/kvm_minidump_i386.c,v 1.2 2006/06/05 08:51:14
27184728Sraj */
28184728Sraj
29184728Sraj#include <sys/cdefs.h>
30184728Sraj__FBSDID("$FreeBSD$");
31184728Sraj
32184728Sraj/*
33184728Sraj * ARM machine dependent routines for kvm and minidumps.
34184728Sraj */
35184728Sraj
36184728Sraj#include <sys/param.h>
37184728Sraj#include <sys/user.h>
38184728Sraj#include <sys/proc.h>
39184728Sraj#include <sys/stat.h>
40184728Sraj#include <sys/mman.h>
41184728Sraj#include <sys/fnv_hash.h>
42184728Sraj#include <stdlib.h>
43184728Sraj#include <string.h>
44184728Sraj#include <unistd.h>
45184728Sraj#include <nlist.h>
46184728Sraj#include <kvm.h>
47184728Sraj
48184728Sraj#include <vm/vm.h>
49184728Sraj#include <vm/vm_param.h>
50184728Sraj
51184728Sraj#include <machine/elf.h>
52184728Sraj#include <machine/cpufunc.h>
53184728Sraj#include <machine/minidump.h>
54184728Sraj
55184728Sraj#include <limits.h>
56184728Sraj
57184728Sraj#include "kvm_private.h"
58184728Sraj
59184728Srajstruct hpte {
60184728Sraj	struct hpte	*next;
61184728Sraj	uint64_t	pa;
62184728Sraj	int64_t		off;
63184728Sraj};
64184728Sraj
65184728Sraj#define HPT_SIZE 1024
66184728Sraj
67184728Sraj/* minidump must be the first field */
68184728Srajstruct vmstate {
69184728Sraj	int		minidump;		/* 1 = minidump mode */
70184728Sraj	struct		minidumphdr hdr;
71184728Sraj	void		*hpt_head[HPT_SIZE];
72184728Sraj	uint32_t	*bitmap;
73184728Sraj	void		*ptemap;
74184728Sraj};
75184728Sraj
76184728Srajstatic void
77184728Srajhpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
78184728Sraj{
79184728Sraj	struct hpte *hpte;
80184728Sraj	uint32_t fnv = FNV1_32_INIT;
81184728Sraj
82184728Sraj	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
83184728Sraj	fnv &= (HPT_SIZE - 1);
84184728Sraj	hpte = malloc(sizeof(*hpte));
85184728Sraj	hpte->pa = pa;
86184728Sraj	hpte->off = off;
87184728Sraj	hpte->next = kd->vmst->hpt_head[fnv];
88184728Sraj	kd->vmst->hpt_head[fnv] = hpte;
89184728Sraj}
90184728Sraj
91184728Srajstatic int64_t
92184728Srajhpt_find(kvm_t *kd, uint64_t pa)
93184728Sraj{
94184728Sraj	struct hpte *hpte;
95184728Sraj	uint32_t fnv = FNV1_32_INIT;
96184728Sraj
97184728Sraj	fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
98184728Sraj	fnv &= (HPT_SIZE - 1);
99184728Sraj	for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next)
100184728Sraj		if (pa == hpte->pa)
101184728Sraj			return (hpte->off);
102184728Sraj
103184728Sraj	return (-1);
104184728Sraj}
105184728Sraj
106184728Srajstatic int
107184728Srajinithash(kvm_t *kd, uint32_t *base, int len, off_t off)
108184728Sraj{
109184728Sraj	uint64_t idx, pa;
110184728Sraj	uint32_t bit, bits;
111184728Sraj
112184728Sraj	for (idx = 0; idx < len / sizeof(*base); idx++) {
113184728Sraj		bits = base[idx];
114184728Sraj		while (bits) {
115184728Sraj			bit = ffs(bits) - 1;
116184728Sraj			bits &= ~(1ul << bit);
117184728Sraj			pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
118184728Sraj			hpt_insert(kd, pa, off);
119184728Sraj			off += PAGE_SIZE;
120184728Sraj		}
121184728Sraj	}
122184728Sraj	return (off);
123184728Sraj}
124184728Sraj
125184728Srajvoid
126184728Sraj_kvm_minidump_freevtop(kvm_t *kd)
127184728Sraj{
128184728Sraj	struct vmstate *vm = kd->vmst;
129184728Sraj
130184728Sraj	if (vm->bitmap)
131184728Sraj		free(vm->bitmap);
132184728Sraj	if (vm->ptemap)
133184728Sraj		free(vm->ptemap);
134184728Sraj	free(vm);
135184728Sraj	kd->vmst = NULL;
136184728Sraj}
137184728Sraj
138184728Srajint
139184728Sraj_kvm_minidump_initvtop(kvm_t *kd)
140184728Sraj{
141184728Sraj	struct vmstate *vmst;
142184728Sraj	off_t off;
143184728Sraj
144184728Sraj	vmst = _kvm_malloc(kd, sizeof(*vmst));
145184728Sraj	if (vmst == 0) {
146184728Sraj		_kvm_err(kd, kd->program, "cannot allocate vm");
147184728Sraj		return (-1);
148184728Sraj	}
149184728Sraj
150184728Sraj	kd->vmst = vmst;
151184728Sraj	vmst->minidump = 1;
152184728Sraj
153184728Sraj	if (pread(kd->pmfd, &vmst->hdr,
154184728Sraj	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
155184728Sraj		_kvm_err(kd, kd->program, "cannot read dump header");
156184728Sraj		return (-1);
157184728Sraj	}
158184728Sraj
159184728Sraj	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
160184728Sraj	    sizeof(vmst->hdr.magic)) != 0) {
161184728Sraj		_kvm_err(kd, kd->program, "not a minidump for this platform");
162184728Sraj		return (-1);
163184728Sraj	}
164184728Sraj	if (vmst->hdr.version != MINIDUMP_VERSION) {
165184728Sraj		_kvm_err(kd, kd->program, "wrong minidump version. "
166184728Sraj		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
167184728Sraj		return (-1);
168184728Sraj	}
169184728Sraj
170184728Sraj	/* Skip header and msgbuf */
171184728Sraj	off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
172184728Sraj
173184728Sraj	vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
174184728Sraj	if (vmst->bitmap == NULL) {
175184728Sraj		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
176184728Sraj		    "bitmap", vmst->hdr.bitmapsize);
177184728Sraj		return (-1);
178184728Sraj	}
179184728Sraj
180184728Sraj	if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
181217744Suqs	    (ssize_t)vmst->hdr.bitmapsize) {
182184728Sraj		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
183184728Sraj		    vmst->hdr.bitmapsize);
184184728Sraj		return (-1);
185184728Sraj	}
186184728Sraj	off += round_page(vmst->hdr.bitmapsize);
187184728Sraj
188184728Sraj	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
189184728Sraj	if (vmst->ptemap == NULL) {
190184728Sraj		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
191184728Sraj		    "ptemap", vmst->hdr.ptesize);
192184728Sraj		return (-1);
193184728Sraj	}
194184728Sraj
195184728Sraj	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
196217744Suqs	    (ssize_t)vmst->hdr.ptesize) {
197184728Sraj		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
198184728Sraj		    vmst->hdr.ptesize);
199184728Sraj		return (-1);
200184728Sraj	}
201184728Sraj
202184728Sraj	off += vmst->hdr.ptesize;
203184728Sraj
204184728Sraj	/* Build physical address hash table for sparse pages */
205184728Sraj	inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
206184728Sraj
207184728Sraj	return (0);
208184728Sraj}
209184728Sraj
210184728Srajint
211184728Sraj_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
212184728Sraj{
213184728Sraj	struct vmstate *vm;
214184728Sraj	pt_entry_t pte;
215184728Sraj	u_long offset, pteindex, a;
216184728Sraj	off_t ofs;
217184728Sraj	uint32_t *ptemap;
218184728Sraj
219184728Sraj	if (ISALIVE(kd)) {
220184728Sraj		_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
221184728Sraj		return (0);
222184728Sraj	}
223184728Sraj
224184728Sraj	vm = kd->vmst;
225184728Sraj	ptemap = vm->ptemap;
226184728Sraj
227184728Sraj	if (va >= vm->hdr.kernbase) {
228184728Sraj		pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
229184728Sraj		pte = ptemap[pteindex];
230184728Sraj		if (!pte) {
231184728Sraj			_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
232184728Sraj			goto invalid;
233184728Sraj		}
234184728Sraj		if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
235184728Sraj			offset = va & L2_L_OFFSET;
236184728Sraj			a = pte & L2_L_FRAME;
237184728Sraj		} else if ((pte & L2_TYPE_MASK) == L2_TYPE_S) {
238184728Sraj			offset = va & L2_S_OFFSET;
239184728Sraj			a = pte & L2_S_FRAME;
240184728Sraj		} else
241184728Sraj			goto invalid;
242184728Sraj
243184728Sraj		ofs = hpt_find(kd, a);
244184728Sraj		if (ofs == -1) {
245184728Sraj			_kvm_err(kd, kd->program, "_kvm_vatop: physical "
246184728Sraj			    "address 0x%lx not in minidump", a);
247184728Sraj			goto invalid;
248184728Sraj		}
249184728Sraj
250184728Sraj		*pa = ofs + offset;
251184728Sraj		return (PAGE_SIZE - offset);
252184728Sraj
253184728Sraj	} else
254184728Sraj		_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
255184728Sraj		    "not minidumped", va);
256184728Sraj
257184728Srajinvalid:
258184728Sraj	_kvm_err(kd, 0, "invalid address (0x%lx)", va);
259184728Sraj	return (0);
260184728Sraj}
261