1/*-
2 * Copyright (c) 1993 Jan-Simon Pendry
3 * Copyright (c) 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Jan-Simon Pendry.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *	@(#)procfs_status.c	8.3 (Berkeley) 2/17/94
34 *
35 * $FreeBSD$
36 */
37
38#include "opt_compat.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/lock.h>
43#include <sys/filedesc.h>
44#include <sys/malloc.h>
45#include <sys/mount.h>
46#include <sys/proc.h>
47#include <sys/resourcevar.h>
48#include <sys/rwlock.h>
49#include <sys/sbuf.h>
50#ifdef COMPAT_FREEBSD32
51#include <sys/sysent.h>
52#endif
53#include <sys/uio.h>
54#include <sys/vnode.h>
55
56#include <fs/pseudofs/pseudofs.h>
57#include <fs/procfs/procfs.h>
58
59#include <vm/vm.h>
60#include <vm/vm_extern.h>
61#include <vm/pmap.h>
62#include <vm/vm_map.h>
63#include <vm/vm_page.h>
64#include <vm/vm_object.h>
65
66#define MEBUFFERSIZE 256
67
68/*
69 * The map entries can *almost* be read with programs like cat.  However,
70 * large maps need special programs to read.  It is not easy to implement
71 * a program that can sense the required size of the buffer, and then
72 * subsequently do a read with the appropriate size.  This operation cannot
73 * be atomic.  The best that we can do is to allow the program to do a read
74 * with an arbitrarily large buffer, and return as much as we can.  We can
75 * return an error code if the buffer is too small (EFBIG), then the program
76 * can try a bigger buffer.
77 */
78int
79procfs_doprocmap(PFS_FILL_ARGS)
80{
81	struct vmspace *vm;
82	vm_map_t map;
83	vm_map_entry_t entry, tmp_entry;
84	struct vnode *vp;
85	char *fullpath, *freepath;
86	struct ucred *cred;
87	int error;
88	unsigned int last_timestamp;
89#ifdef COMPAT_FREEBSD32
90	int wrap32 = 0;
91#endif
92
93	PROC_LOCK(p);
94	error = p_candebug(td, p);
95	PROC_UNLOCK(p);
96	if (error)
97		return (error);
98
99	if (uio->uio_rw != UIO_READ)
100		return (EOPNOTSUPP);
101
102#ifdef COMPAT_FREEBSD32
103        if (SV_CURPROC_FLAG(SV_ILP32)) {
104                if (!(SV_PROC_FLAG(p, SV_ILP32)))
105                        return (EOPNOTSUPP);
106                wrap32 = 1;
107        }
108#endif
109
110	vm = vmspace_acquire_ref(p);
111	if (vm == NULL)
112		return (ESRCH);
113	map = &vm->vm_map;
114	vm_map_lock_read(map);
115	for (entry = map->header.next; entry != &map->header;
116	     entry = entry->next) {
117		vm_object_t obj, tobj, lobj;
118		int ref_count, shadow_count, flags;
119		vm_offset_t e_start, e_end, addr;
120		int resident, privateresident;
121		char *type;
122		vm_eflags_t e_eflags;
123		vm_prot_t e_prot;
124
125		if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
126			continue;
127
128		e_eflags = entry->eflags;
129		e_prot = entry->protection;
130		e_start = entry->start;
131		e_end = entry->end;
132		privateresident = 0;
133		obj = entry->object.vm_object;
134		if (obj != NULL) {
135			VM_OBJECT_RLOCK(obj);
136			if (obj->shadow_count == 1)
137				privateresident = obj->resident_page_count;
138		}
139		cred = (entry->cred) ? entry->cred : (obj ? obj->cred : NULL);
140
141		resident = 0;
142		addr = entry->start;
143		while (addr < entry->end) {
144			if (pmap_extract(map->pmap, addr))
145				resident++;
146			addr += PAGE_SIZE;
147		}
148
149		for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) {
150			if (tobj != obj)
151				VM_OBJECT_RLOCK(tobj);
152			if (lobj != obj)
153				VM_OBJECT_RUNLOCK(lobj);
154			lobj = tobj;
155		}
156		last_timestamp = map->timestamp;
157		vm_map_unlock_read(map);
158
159		freepath = NULL;
160		fullpath = "-";
161		if (lobj) {
162			vp = NULL;
163			switch (lobj->type) {
164			default:
165			case OBJT_DEFAULT:
166				type = "default";
167				break;
168			case OBJT_VNODE:
169				type = "vnode";
170				vp = lobj->handle;
171				vref(vp);
172				break;
173			case OBJT_SWAP:
174				if ((lobj->flags & OBJ_TMPFS_NODE) != 0) {
175					type = "vnode";
176					if ((lobj->flags & OBJ_TMPFS) != 0) {
177						vp = lobj->un_pager.swp.swp_tmpfs;
178						vref(vp);
179					}
180				} else {
181					type = "swap";
182				}
183				break;
184			case OBJT_SG:
185			case OBJT_DEVICE:
186				type = "device";
187				break;
188			}
189			if (lobj != obj)
190				VM_OBJECT_RUNLOCK(lobj);
191
192			flags = obj->flags;
193			ref_count = obj->ref_count;
194			shadow_count = obj->shadow_count;
195			VM_OBJECT_RUNLOCK(obj);
196			if (vp != NULL) {
197				vn_fullpath(td, vp, &fullpath, &freepath);
198				vrele(vp);
199			}
200		} else {
201			type = "none";
202			flags = 0;
203			ref_count = 0;
204			shadow_count = 0;
205		}
206
207		/*
208		 * format:
209		 *  start, end, resident, private resident, cow, access, type,
210		 *         charged, charged uid.
211		 */
212		error = sbuf_printf(sb,
213		    "0x%lx 0x%lx %d %d %p %s%s%s %d %d 0x%x %s %s %s %s %s %d\n",
214			(u_long)e_start, (u_long)e_end,
215			resident, privateresident,
216#ifdef COMPAT_FREEBSD32
217			wrap32 ? NULL : obj,	/* Hide 64 bit value */
218#else
219			obj,
220#endif
221			(e_prot & VM_PROT_READ)?"r":"-",
222			(e_prot & VM_PROT_WRITE)?"w":"-",
223			(e_prot & VM_PROT_EXECUTE)?"x":"-",
224			ref_count, shadow_count, flags,
225			(e_eflags & MAP_ENTRY_COW)?"COW":"NCOW",
226			(e_eflags & MAP_ENTRY_NEEDS_COPY)?"NC":"NNC",
227			type, fullpath,
228			cred ? "CH":"NCH", cred ? cred->cr_ruid : -1);
229
230		if (freepath != NULL)
231			free(freepath, M_TEMP);
232		vm_map_lock_read(map);
233		if (error == -1) {
234			error = 0;
235			break;
236		}
237		if (last_timestamp != map->timestamp) {
238			/*
239			 * Look again for the entry because the map was
240			 * modified while it was unlocked.  Specifically,
241			 * the entry may have been clipped, merged, or deleted.
242			 */
243			vm_map_lookup_entry(map, e_end - 1, &tmp_entry);
244			entry = tmp_entry;
245		}
246	}
247	vm_map_unlock_read(map);
248	vmspace_free(vm);
249	return (error);
250}
251