17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5a85a6733Sjosephb  * Common Development and Distribution License (the "License").
6a85a6733Sjosephb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22cbdcbd05SJonathan Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2474ecdb51SJohn Levon  *
25*3b442230SJordan Paige Hendricks  * Copyright 2019 Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * This part of the file contains the mdb support for dcmds:
307c478bd9Sstevel@tonic-gate  *	::memseg_list
317c478bd9Sstevel@tonic-gate  * and walkers for:
327c478bd9Sstevel@tonic-gate  *	memseg - a memseg list walker for ::memseg_list
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #include <sys/types.h>
377c478bd9Sstevel@tonic-gate #include <sys/machparam.h>
387c478bd9Sstevel@tonic-gate #include <sys/controlregs.h>
39ae115bc7Smrj #include <sys/mach_mmu.h>
40843e1988Sjohnlev #ifdef __xpv
41843e1988Sjohnlev #include <sys/hypervisor.h>
42843e1988Sjohnlev #endif
437c478bd9Sstevel@tonic-gate #include <vm/as.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
467c478bd9Sstevel@tonic-gate #include <mdb/mdb_target.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <vm/page.h>
497c478bd9Sstevel@tonic-gate #include <vm/hat_i86.h>
507c478bd9Sstevel@tonic-gate 
5174ecdb51SJohn Levon #define	VA_SIGN_BIT (1UL << 47)
5274ecdb51SJohn Levon #define	VA_SIGN_EXTEND(va) (((va) ^ VA_SIGN_BIT) - VA_SIGN_BIT)
5374ecdb51SJohn Levon 
547c478bd9Sstevel@tonic-gate struct pfn2pp {
557c478bd9Sstevel@tonic-gate 	pfn_t pfn;
567c478bd9Sstevel@tonic-gate 	page_t *pp;
577c478bd9Sstevel@tonic-gate };
587c478bd9Sstevel@tonic-gate 
59ae115bc7Smrj static int do_va2pa(uintptr_t, struct as *, int, physaddr_t *, pfn_t *);
60843e1988Sjohnlev static void init_mmu(void);
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate int
platform_vtop(uintptr_t addr,struct as * asp,physaddr_t * pap)637c478bd9Sstevel@tonic-gate platform_vtop(uintptr_t addr, struct as *asp, physaddr_t *pap)
647c478bd9Sstevel@tonic-gate {
657c478bd9Sstevel@tonic-gate 	if (asp == NULL)
667c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
677c478bd9Sstevel@tonic-gate 
68843e1988Sjohnlev 	init_mmu();
69843e1988Sjohnlev 
707c478bd9Sstevel@tonic-gate 	if (mmu.num_level == 0)
717c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
727c478bd9Sstevel@tonic-gate 
73ae115bc7Smrj 	return (do_va2pa(addr, asp, 0, pap, NULL));
747c478bd9Sstevel@tonic-gate }
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * ::memseg_list dcmd and walker to implement it.
787c478bd9Sstevel@tonic-gate  */
797c478bd9Sstevel@tonic-gate /*ARGSUSED*/
807c478bd9Sstevel@tonic-gate int
memseg_list(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)817c478bd9Sstevel@tonic-gate memseg_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
827c478bd9Sstevel@tonic-gate {
837c478bd9Sstevel@tonic-gate 	struct memseg ms;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
867c478bd9Sstevel@tonic-gate 		if (mdb_pwalk_dcmd("memseg", "memseg_list",
877c478bd9Sstevel@tonic-gate 		    0, NULL, 0) == -1) {
887c478bd9Sstevel@tonic-gate 			mdb_warn("can't walk memseg");
897c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
907c478bd9Sstevel@tonic-gate 		}
917c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
927c478bd9Sstevel@tonic-gate 	}
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
957c478bd9Sstevel@tonic-gate 		mdb_printf("%<u>%?s %?s %?s %?s %?s%</u>\n", "ADDR",
96843e1988Sjohnlev 		    "PAGES", "EPAGES", "BASE", "END");
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	if (mdb_vread(&ms, sizeof (struct memseg), addr) == -1) {
997c478bd9Sstevel@tonic-gate 		mdb_warn("can't read memseg at %#lx", addr);
1007c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
1017c478bd9Sstevel@tonic-gate 	}
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	mdb_printf("%0?lx %0?lx %0?lx %0?lx %0?lx\n", addr,
104843e1988Sjohnlev 	    ms.pages, ms.epages, ms.pages_base, ms.pages_end);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate  * walk the memseg structures
1117c478bd9Sstevel@tonic-gate  */
1127c478bd9Sstevel@tonic-gate int
memseg_walk_init(mdb_walk_state_t * wsp)1137c478bd9Sstevel@tonic-gate memseg_walk_init(mdb_walk_state_t *wsp)
1147c478bd9Sstevel@tonic-gate {
115892ad162SToomas Soome 	if (wsp->walk_addr != 0) {
1167c478bd9Sstevel@tonic-gate 		mdb_warn("memseg only supports global walks\n");
1177c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1187c478bd9Sstevel@tonic-gate 	}
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	if (mdb_readvar(&wsp->walk_addr, "memsegs") == -1) {
1217c478bd9Sstevel@tonic-gate 		mdb_warn("symbol 'memsegs' not found");
1227c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1237c478bd9Sstevel@tonic-gate 	}
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	wsp->walk_data = mdb_alloc(sizeof (struct memseg), UM_SLEEP);
1267c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate }
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate int
memseg_walk_step(mdb_walk_state_t * wsp)1317c478bd9Sstevel@tonic-gate memseg_walk_step(mdb_walk_state_t *wsp)
1327c478bd9Sstevel@tonic-gate {
1337c478bd9Sstevel@tonic-gate 	int status;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	if (wsp->walk_addr == 0) {
1367c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
1377c478bd9Sstevel@tonic-gate 	}
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	if (mdb_vread(wsp->walk_data, sizeof (struct memseg),
1407c478bd9Sstevel@tonic-gate 	    wsp->walk_addr) == -1) {
1417c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read struct memseg at %p", wsp->walk_addr);
1427c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1467c478bd9Sstevel@tonic-gate 	    wsp->walk_cbdata);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)(((struct memseg *)wsp->walk_data)->next);
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	return (status);
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate void
memseg_walk_fini(mdb_walk_state_t * wsp)1547c478bd9Sstevel@tonic-gate memseg_walk_fini(mdb_walk_state_t *wsp)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	mdb_free(wsp->walk_data, sizeof (struct memseg));
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate /*
160ae115bc7Smrj  * Now HAT related dcmds.
1617c478bd9Sstevel@tonic-gate  */
1627c478bd9Sstevel@tonic-gate 
163843e1988Sjohnlev static struct hat *khat;		/* value of kas.a_hat */
1647c478bd9Sstevel@tonic-gate struct hat_mmu_info mmu;
1657c478bd9Sstevel@tonic-gate uintptr_t kernelbase;
1667c478bd9Sstevel@tonic-gate 
167843e1988Sjohnlev /*
168843e1988Sjohnlev  * stuff for i86xpv images
169843e1988Sjohnlev  */
170843e1988Sjohnlev static int is_xpv;
171843e1988Sjohnlev static uintptr_t mfn_list_addr; /* kernel MFN list address */
172843e1988Sjohnlev uintptr_t xen_virt_start; /* address of mfn_to_pfn[] table */
173843e1988Sjohnlev ulong_t mfn_count;	/* number of pfn's in the MFN list */
174843e1988Sjohnlev pfn_t *mfn_list;	/* local MFN list copy */
175843e1988Sjohnlev 
1767c478bd9Sstevel@tonic-gate /*
1777c478bd9Sstevel@tonic-gate  * read mmu parameters from kernel
1787c478bd9Sstevel@tonic-gate  */
1797c478bd9Sstevel@tonic-gate static void
init_mmu(void)180843e1988Sjohnlev init_mmu(void)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	struct as kas;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	if (mmu.num_level != 0)
1857c478bd9Sstevel@tonic-gate 		return;
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	if (mdb_readsym(&mmu, sizeof (mmu), "mmu") == -1)
1887c478bd9Sstevel@tonic-gate 		mdb_warn("Can't use HAT information before mmu_init()\n");
1897c478bd9Sstevel@tonic-gate 	if (mdb_readsym(&kas, sizeof (kas), "kas") == -1)
1907c478bd9Sstevel@tonic-gate 		mdb_warn("Couldn't find kas - kernel's struct as\n");
1917c478bd9Sstevel@tonic-gate 	if (mdb_readsym(&kernelbase, sizeof (kernelbase), "kernelbase") == -1)
1927c478bd9Sstevel@tonic-gate 		mdb_warn("Couldn't find kernelbase\n");
1937c478bd9Sstevel@tonic-gate 	khat = kas.a_hat;
194843e1988Sjohnlev 
195843e1988Sjohnlev 	/*
196843e1988Sjohnlev 	 * Is this a paravirtualized domain image?
197843e1988Sjohnlev 	 */
198843e1988Sjohnlev 	if (mdb_readsym(&mfn_list_addr, sizeof (mfn_list_addr),
199843e1988Sjohnlev 	    "mfn_list") == -1 ||
200843e1988Sjohnlev 	    mdb_readsym(&xen_virt_start, sizeof (xen_virt_start),
201843e1988Sjohnlev 	    "xen_virt_start") == -1 ||
202843e1988Sjohnlev 	    mdb_readsym(&mfn_count, sizeof (mfn_count), "mfn_count") == -1) {
203892ad162SToomas Soome 		mfn_list_addr = 0;
204843e1988Sjohnlev 	}
205843e1988Sjohnlev 
206892ad162SToomas Soome 	is_xpv = mfn_list_addr != 0;
207843e1988Sjohnlev 
208843e1988Sjohnlev #ifndef _KMDB
209843e1988Sjohnlev 	/*
210843e1988Sjohnlev 	 * recreate the local mfn_list
211843e1988Sjohnlev 	 */
212843e1988Sjohnlev 	if (is_xpv) {
213843e1988Sjohnlev 		size_t sz = mfn_count * sizeof (pfn_t);
214843e1988Sjohnlev 		mfn_list = mdb_zalloc(sz, UM_SLEEP);
215843e1988Sjohnlev 
216843e1988Sjohnlev 		if (mdb_vread(mfn_list, sz, (uintptr_t)mfn_list_addr) == -1) {
217843e1988Sjohnlev 			mdb_warn("Failed to read MFN list\n");
218843e1988Sjohnlev 			mdb_free(mfn_list, sz);
219843e1988Sjohnlev 			mfn_list = NULL;
220843e1988Sjohnlev 		}
221843e1988Sjohnlev 	}
222843e1988Sjohnlev #endif
223843e1988Sjohnlev }
224843e1988Sjohnlev 
225843e1988Sjohnlev void
free_mmu(void)226843e1988Sjohnlev free_mmu(void)
227843e1988Sjohnlev {
228843e1988Sjohnlev #ifdef __xpv
229843e1988Sjohnlev 	if (mfn_list != NULL)
230843e1988Sjohnlev 		mdb_free(mfn_list, mfn_count * sizeof (mfn_t));
231843e1988Sjohnlev #endif
232843e1988Sjohnlev }
233843e1988Sjohnlev 
234843e1988Sjohnlev #ifdef __xpv
235843e1988Sjohnlev 
236843e1988Sjohnlev #ifdef _KMDB
237843e1988Sjohnlev 
238843e1988Sjohnlev /*
239843e1988Sjohnlev  * Convert between MFNs and PFNs.  Since we're in kmdb we can go directly
240843e1988Sjohnlev  * through the machine to phys mapping and the MFN list.
241843e1988Sjohnlev  */
242843e1988Sjohnlev 
243843e1988Sjohnlev pfn_t
mdb_mfn_to_pfn(mfn_t mfn)244843e1988Sjohnlev mdb_mfn_to_pfn(mfn_t mfn)
245843e1988Sjohnlev {
246843e1988Sjohnlev 	pfn_t pfn;
247843e1988Sjohnlev 	mfn_t tmp;
248843e1988Sjohnlev 	pfn_t *pfn_list;
249843e1988Sjohnlev 
250892ad162SToomas Soome 	if (mfn_list_addr == 0)
251843e1988Sjohnlev 		return (-(pfn_t)1);
252843e1988Sjohnlev 
253843e1988Sjohnlev 	pfn_list = (pfn_t *)xen_virt_start;
254843e1988Sjohnlev 	if (mdb_vread(&pfn, sizeof (pfn), (uintptr_t)(pfn_list + mfn)) == -1)
255843e1988Sjohnlev 		return (-(pfn_t)1);
256843e1988Sjohnlev 
257843e1988Sjohnlev 	if (mdb_vread(&tmp, sizeof (tmp),
258843e1988Sjohnlev 	    (uintptr_t)(mfn_list_addr + (pfn * sizeof (mfn_t)))) == -1)
259843e1988Sjohnlev 		return (-(pfn_t)1);
260843e1988Sjohnlev 
261843e1988Sjohnlev 	if (pfn >= mfn_count || tmp != mfn)
262843e1988Sjohnlev 		return (-(pfn_t)1);
263843e1988Sjohnlev 
264843e1988Sjohnlev 	return (pfn);
265843e1988Sjohnlev }
266843e1988Sjohnlev 
267843e1988Sjohnlev mfn_t
mdb_pfn_to_mfn(pfn_t pfn)268843e1988Sjohnlev mdb_pfn_to_mfn(pfn_t pfn)
269843e1988Sjohnlev {
270843e1988Sjohnlev 	mfn_t mfn;
271843e1988Sjohnlev 
272843e1988Sjohnlev 	init_mmu();
273843e1988Sjohnlev 
274892ad162SToomas Soome 	if (mfn_list_addr == 0 || pfn >= mfn_count)
275843e1988Sjohnlev 		return (-(mfn_t)1);
276843e1988Sjohnlev 
277843e1988Sjohnlev 	if (mdb_vread(&mfn, sizeof (mfn),
278843e1988Sjohnlev 	    (uintptr_t)(mfn_list_addr + (pfn * sizeof (mfn_t)))) == -1)
279843e1988Sjohnlev 		return (-(mfn_t)1);
280843e1988Sjohnlev 
281843e1988Sjohnlev 	return (mfn);
282843e1988Sjohnlev }
283843e1988Sjohnlev 
284843e1988Sjohnlev #else /* _KMDB */
285843e1988Sjohnlev 
286843e1988Sjohnlev /*
287843e1988Sjohnlev  * Convert between MFNs and PFNs.  Since a crash dump doesn't include the
288843e1988Sjohnlev  * MFN->PFN translation table (it's part of the hypervisor, not our image)
289843e1988Sjohnlev  * we do the MFN->PFN translation by searching the PFN->MFN (mfn_list)
290843e1988Sjohnlev  * table, if it's there.
291843e1988Sjohnlev  */
292843e1988Sjohnlev 
293843e1988Sjohnlev pfn_t
mdb_mfn_to_pfn(mfn_t mfn)294843e1988Sjohnlev mdb_mfn_to_pfn(mfn_t mfn)
295843e1988Sjohnlev {
296843e1988Sjohnlev 	pfn_t pfn;
297843e1988Sjohnlev 
298843e1988Sjohnlev 	init_mmu();
299843e1988Sjohnlev 
300843e1988Sjohnlev 	if (mfn_list == NULL)
301843e1988Sjohnlev 		return (-(pfn_t)1);
302843e1988Sjohnlev 
303843e1988Sjohnlev 	for (pfn = 0; pfn < mfn_count; ++pfn) {
304843e1988Sjohnlev 		if (mfn_list[pfn] != mfn)
305843e1988Sjohnlev 			continue;
306843e1988Sjohnlev 		return (pfn);
307843e1988Sjohnlev 	}
308843e1988Sjohnlev 
309843e1988Sjohnlev 	return (-(pfn_t)1);
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
312843e1988Sjohnlev mfn_t
mdb_pfn_to_mfn(pfn_t pfn)313843e1988Sjohnlev mdb_pfn_to_mfn(pfn_t pfn)
314843e1988Sjohnlev {
315843e1988Sjohnlev 	init_mmu();
316843e1988Sjohnlev 
317843e1988Sjohnlev 	if (mfn_list == NULL || pfn >= mfn_count)
318843e1988Sjohnlev 		return (-(mfn_t)1);
319843e1988Sjohnlev 
320843e1988Sjohnlev 	return (mfn_list[pfn]);
321843e1988Sjohnlev }
322843e1988Sjohnlev 
323843e1988Sjohnlev #endif /* _KMDB */
324843e1988Sjohnlev 
325843e1988Sjohnlev static paddr_t
mdb_ma_to_pa(uint64_t ma)326843e1988Sjohnlev mdb_ma_to_pa(uint64_t ma)
327843e1988Sjohnlev {
328843e1988Sjohnlev 	pfn_t pfn = mdb_mfn_to_pfn(mmu_btop(ma));
329843e1988Sjohnlev 	if (pfn == -(pfn_t)1)
330843e1988Sjohnlev 		return (-(paddr_t)1);
331843e1988Sjohnlev 
332843e1988Sjohnlev 	return (mmu_ptob((paddr_t)pfn) | (ma & (MMU_PAGESIZE - 1)));
333843e1988Sjohnlev }
334843e1988Sjohnlev 
335843e1988Sjohnlev #else /* __xpv */
336843e1988Sjohnlev 
337ae115bc7Smrj #define	mdb_ma_to_pa(ma) (ma)
338ae115bc7Smrj #define	mdb_mfn_to_pfn(mfn) (mfn)
339