kvm_minidump_arm.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008 Semihalf, Grzegorz Bernacki
5 * Copyright (c) 2006 Peter Wemm
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * From: FreeBSD: src/lib/libkvm/kvm_minidump_i386.c,v 1.2 2006/06/05 08:51:14
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/11/lib/libkvm/kvm_minidump_arm.c 330897 2018-03-14 03:19:51Z eadler $");
33
34/*
35 * ARM machine dependent routines for kvm and minidumps.
36 */
37
38#include <sys/endian.h>
39#include <sys/param.h>
40#include <kvm.h>
41#include <limits.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "../../sys/arm/include/minidump.h"
48
49#include "kvm_private.h"
50#include "kvm_arm.h"
51
52#define	arm_round_page(x)	roundup2((kvaddr_t)(x), ARM_PAGE_SIZE)
53
54struct vmstate {
55	struct		minidumphdr hdr;
56	struct		hpt hpt;
57	void		*ptemap;
58	unsigned char	ei_data;
59};
60
61static int
62_arm_minidump_probe(kvm_t *kd)
63{
64
65	return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_ARM) &&
66	    _kvm_is_minidump(kd));
67}
68
69static void
70_arm_minidump_freevtop(kvm_t *kd)
71{
72	struct vmstate *vm = kd->vmst;
73
74	_kvm_hpt_free(&vm->hpt);
75	if (vm->ptemap)
76		free(vm->ptemap);
77	free(vm);
78	kd->vmst = NULL;
79}
80
81static int
82_arm_minidump_initvtop(kvm_t *kd)
83{
84	struct vmstate *vmst;
85	uint32_t *bitmap;
86	off_t off;
87
88	vmst = _kvm_malloc(kd, sizeof(*vmst));
89	if (vmst == NULL) {
90		_kvm_err(kd, kd->program, "cannot allocate vm");
91		return (-1);
92	}
93
94	kd->vmst = vmst;
95
96	if (pread(kd->pmfd, &vmst->hdr,
97	    sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
98		_kvm_err(kd, kd->program, "cannot read dump header");
99		return (-1);
100	}
101
102	if (strncmp(MINIDUMP_MAGIC, vmst->hdr.magic,
103	    sizeof(vmst->hdr.magic)) != 0) {
104		_kvm_err(kd, kd->program, "not a minidump for this platform");
105		return (-1);
106	}
107	vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
108	if (vmst->hdr.version != MINIDUMP_VERSION) {
109		_kvm_err(kd, kd->program, "wrong minidump version. "
110		    "Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
111		return (-1);
112	}
113	vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
114	vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
115	vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
116	vmst->hdr.kernbase = _kvm32toh(kd, vmst->hdr.kernbase);
117	vmst->hdr.arch = _kvm32toh(kd, vmst->hdr.arch);
118	vmst->hdr.mmuformat = _kvm32toh(kd, vmst->hdr.mmuformat);
119	if (vmst->hdr.mmuformat == MINIDUMP_MMU_FORMAT_UNKNOWN) {
120		/* This is a safe default as 1K pages are not used. */
121		vmst->hdr.mmuformat = MINIDUMP_MMU_FORMAT_V6;
122	}
123
124	/* Skip header and msgbuf */
125	off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize);
126
127	bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
128	if (bitmap == NULL) {
129		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
130		    "bitmap", vmst->hdr.bitmapsize);
131		return (-1);
132	}
133
134	if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
135	    (ssize_t)vmst->hdr.bitmapsize) {
136		_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
137		    vmst->hdr.bitmapsize);
138		free(bitmap);
139		return (-1);
140	}
141	off += arm_round_page(vmst->hdr.bitmapsize);
142
143	vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
144	if (vmst->ptemap == NULL) {
145		_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
146		    "ptemap", vmst->hdr.ptesize);
147		free(bitmap);
148		return (-1);
149	}
150
151	if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
152	    (ssize_t)vmst->hdr.ptesize) {
153		_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
154		    vmst->hdr.ptesize);
155		free(bitmap);
156		return (-1);
157	}
158
159	off += vmst->hdr.ptesize;
160
161	/* Build physical address hash table for sparse pages */
162	_kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
163	    ARM_PAGE_SIZE, sizeof(*bitmap));
164	free(bitmap);
165
166	return (0);
167}
168
169static int
170_arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
171{
172	struct vmstate *vm;
173	arm_pt_entry_t pte;
174	arm_physaddr_t offset, a;
175	kvaddr_t pteindex;
176	off_t ofs;
177	arm_pt_entry_t *ptemap;
178
179	if (ISALIVE(kd)) {
180		_kvm_err(kd, 0, "_arm_minidump_kvatop called in live kernel!");
181		return (0);
182	}
183
184	vm = kd->vmst;
185	ptemap = vm->ptemap;
186
187	if (va >= vm->hdr.kernbase) {
188		pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT;
189		pte = _kvm32toh(kd, ptemap[pteindex]);
190		if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV) {
191			_kvm_err(kd, kd->program,
192			    "_arm_minidump_kvatop: pte not valid");
193			goto invalid;
194		}
195		if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
196			/* 64K page -> convert to be like 4K page */
197			offset = va & ARM_L2_S_OFFSET;
198			a = (pte & ARM_L2_L_FRAME) +
199			    (va & ARM_L2_L_OFFSET & ARM_L2_S_FRAME);
200		} else {
201			if (kd->vmst->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4 &&
202			    (pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_T) {
203				_kvm_err(kd, kd->program,
204				    "_arm_minidump_kvatop: pte not supported");
205				goto invalid;
206			}
207			/* 4K page */
208			offset = va & ARM_L2_S_OFFSET;
209			a = pte & ARM_L2_S_FRAME;
210		}
211
212		ofs = _kvm_hpt_find(&vm->hpt, a);
213		if (ofs == -1) {
214			_kvm_err(kd, kd->program, "_arm_minidump_kvatop: "
215			    "physical address 0x%jx not in minidump",
216			    (uintmax_t)a);
217			goto invalid;
218		}
219
220		*pa = ofs + offset;
221		return (ARM_PAGE_SIZE - offset);
222	} else
223		_kvm_err(kd, kd->program, "_arm_minidump_kvatop: virtual "
224		    "address 0x%jx not minidumped", (uintmax_t)va);
225
226invalid:
227	_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
228	return (0);
229}
230
231static struct kvm_arch kvm_arm_minidump = {
232	.ka_probe = _arm_minidump_probe,
233	.ka_initvtop = _arm_minidump_initvtop,
234	.ka_freevtop = _arm_minidump_freevtop,
235	.ka_kvatop = _arm_minidump_kvatop,
236	.ka_native = _arm_native,
237};
238
239KVM_ARCH(kvm_arm_minidump);
240