memcontrol.c revision 139854
1/*-
2 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/usr.sbin/memcontrol/memcontrol.c 139854 2005-01-07 12:06:30Z delphij $
27 */
28
29#include <sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/memrange.h>
32
33#include <err.h>
34#include <fcntl.h>
35#include <inttypes.h>
36#include <paths.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42struct
43{
44    const char	*name;
45    int		val;
46    int		kind;
47#define MDF_SETTABLE	(1<<0)
48} attrnames[] = {
49    {"uncacheable",	MDF_UNCACHEABLE,	MDF_SETTABLE},
50    {"write-combine",	MDF_WRITECOMBINE,	MDF_SETTABLE},
51    {"write-through",	MDF_WRITETHROUGH,	MDF_SETTABLE},
52    {"write-back",	MDF_WRITEBACK,		MDF_SETTABLE},
53    {"write-protect",	MDF_WRITEPROTECT,	MDF_SETTABLE},
54    {"force",		MDF_FORCE,		MDF_SETTABLE},
55    {"unknown",		MDF_UNKNOWN,		0},
56    {"fixed-base",	MDF_FIXBASE,		0},
57    {"fixed-length",	MDF_FIXLEN,		0},
58    {"set-by-firmware",	MDF_FIRMWARE,		0},
59    {"active",		MDF_ACTIVE,		MDF_SETTABLE},
60    {"bogus",		MDF_BOGUS,		0},
61    {NULL,		0,			0}
62};
63
64static void	listfunc(int memfd, int argc, char *argv[]);
65static void	setfunc(int memfd, int argc, char *argv[]);
66static void	clearfunc(int memfd, int argc, char *argv[]);
67static void	helpfunc(int memfd, int argc, char *argv[]);
68static void	help(const char *what);
69
70struct
71{
72    const char	*cmd;
73    const char	*desc;
74    void	(*func)(int memfd, int argc, char *argv[]);
75} functions[] = {
76    {"list",
77     "List current memory range attributes\n"
78     "    list [-a]\n"
79     "        -a    list all range slots, even those that are inactive",
80     listfunc},
81    {"set",
82     "Set memory range attributes\n"
83     "    set -b <base> -l <length> -o <owner> <attribute>\n"
84     "        <base>      memory range base address\n"
85     "        <length>    length of memory range in bytes, power of 2\n"
86     "        <owner>     text identifier for this setting (7 char max)\n"
87     "        <attribute> attribute(s) to be applied to this range:\n"
88     "                        uncacheable\n"
89     "                        write-combine\n"
90     "                        write-through\n"
91     "                        write-back\n"
92     "                        write-protect",
93     setfunc},
94    {"clear",
95     "Clear memory range attributes\n"
96     "    clear -o <owner>\n"
97     "        <owner>     all ranges with this owner will be cleared\n"
98     "    clear -b <base> -l <length>\n"
99     "        <base>      memory range base address\n"
100     "        <length>    length of memory range in bytes, power of 2\n"
101     "                    Base and length must exactly match an existing range",
102     clearfunc},
103    {NULL,	NULL,					helpfunc}
104};
105
106int
107main(int argc, char *argv[])
108{
109    int		i, memfd;
110
111    if (argc < 2) {
112	help(NULL);
113    } else {
114	if ((memfd = open(_PATH_MEM, O_RDONLY)) == -1)
115	    err(1, "can't open %s", _PATH_MEM);
116
117	for (i = 0; functions[i].cmd != NULL; i++)
118	    if (!strcmp(argv[1], functions[i].cmd))
119		break;
120	functions[i].func(memfd, argc - 1, argv + 1);
121	close(memfd);
122    }
123    return(0);
124}
125
126static struct mem_range_desc *
127mrgetall(int memfd, int *nmr)
128{
129    struct mem_range_desc	*mrd;
130    struct mem_range_op		mro;
131
132    mro.mo_arg[0] = 0;
133    if (ioctl(memfd, MEMRANGE_GET, &mro))
134	err(1, "can't size range descriptor array");
135
136    *nmr = mro.mo_arg[0];
137    mrd = malloc(*nmr * sizeof(struct mem_range_desc));
138    if (mrd == NULL)
139	errx(1, "can't allocate %zd bytes for %d range descriptors",
140	     *nmr * sizeof(struct mem_range_desc), *nmr);
141
142    mro.mo_arg[0] = *nmr;
143    mro.mo_desc = mrd;
144    if (ioctl(memfd, MEMRANGE_GET, &mro))
145	err(1, "can't fetch range descriptor array");
146
147    return(mrd);
148}
149
150
151static void
152listfunc(int memfd, int argc, char *argv[])
153{
154    struct mem_range_desc	*mrd;
155    int				nd, i, j;
156    int				ch;
157    int				showall = 0;
158    char			*owner;
159
160    owner = NULL;
161    while ((ch = getopt(argc, argv, "ao:")) != -1)
162	switch(ch) {
163	case 'a':
164	    showall = 1;
165	    break;
166	case 'o':
167	    owner = strdup(optarg);
168	    break;
169	case '?':
170	default:
171	    help("list");
172	}
173
174    mrd = mrgetall(memfd, &nd);
175
176    for (i = 0; i < nd; i++) {
177	if (!showall && !(mrd[i].mr_flags & MDF_ACTIVE))
178	    continue;
179	if (owner && strcmp(mrd[i].mr_owner, owner))
180	    continue;
181	printf("%" PRIu64 "x/%" PRIu64 "x %.8s ", mrd[i].mr_base, mrd[i].mr_len,
182	       mrd[i].mr_owner[0] ? mrd[i].mr_owner : "-");
183	for (j = 0; attrnames[j].name != NULL; j++)
184	    if (mrd[i].mr_flags & attrnames[j].val)
185		printf("%s ", attrnames[j].name);
186	printf("\n");
187    }
188    free(mrd);
189    if (owner)
190	free(owner);
191}
192
193static void
194setfunc(int memfd, int argc, char *argv[])
195{
196    struct mem_range_desc	mrd;
197    struct mem_range_op		mro;
198    int				i;
199    int				ch;
200    char			*ep;
201
202    mrd.mr_base = 0;
203    mrd.mr_len = 0;
204    mrd.mr_flags = 0;
205    strcpy(mrd.mr_owner, "user");
206    while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
207	switch(ch) {
208	case 'b':
209	    mrd.mr_base = strtouq(optarg, &ep, 0);
210	    if ((ep == optarg) || (*ep != 0))
211		help("set");
212	    break;
213	case 'l':
214	    mrd.mr_len = strtouq(optarg, &ep, 0);
215	    if ((ep == optarg) || (*ep != 0))
216		help("set");
217	    break;
218	case 'o':
219	    if ((*optarg == 0) || (strlen(optarg) > 7))
220		help("set");
221	    strcpy(mrd.mr_owner, optarg);
222	    break;
223
224	case '?':
225	default:
226	    help("set");
227	}
228
229    if (mrd.mr_len == 0)
230	help("set");
231
232    argc -= optind;
233    argv += optind;
234
235    while(argc--) {
236	for (i = 0; attrnames[i].name != NULL; i++) {
237	    if (!strcmp(attrnames[i].name, argv[0])) {
238		if (!attrnames[i].kind & MDF_SETTABLE)
239		    help("flags");
240		mrd.mr_flags |= attrnames[i].val;
241		break;
242	    }
243	}
244	if (attrnames[i].name == NULL)
245	    help("flags");
246	argv++;
247    }
248
249    mro.mo_desc = &mrd;
250    mro.mo_arg[0] = 0;
251    if (ioctl(memfd, MEMRANGE_SET, &mro))
252	err(1, "can't set range");
253}
254
255static void
256clearfunc(int memfd, int argc, char *argv[])
257{
258    struct mem_range_desc	mrd, *mrdp;
259    struct mem_range_op		mro;
260    int				i, nd;
261    int				ch;
262    char			*ep, *owner;
263
264    mrd.mr_base = 0;
265    mrd.mr_len = 0;
266    owner = NULL;
267    while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
268	switch(ch) {
269	case 'b':
270	    mrd.mr_base = strtouq(optarg, &ep, 0);
271	    if ((ep == optarg) || (*ep != 0))
272		help("clear");
273	    break;
274	case 'l':
275	    mrd.mr_len = strtouq(optarg, &ep, 0);
276	    if ((ep == optarg) || (*ep != 0))
277		help("clear");
278	    break;
279	case 'o':
280	    if ((*optarg == 0) || (strlen(optarg) > 7))
281		help("clear");
282	    owner = strdup(optarg);
283	    break;
284
285	case '?':
286	default:
287	    help("clear");
288	}
289
290    if (owner != NULL) {
291	/* clear-by-owner */
292	if ((mrd.mr_base != 0) || (mrd.mr_len != 0))
293	    help("clear");
294
295	mrdp = mrgetall(memfd, &nd);
296	mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
297	for (i = 0; i < nd; i++) {
298	    if (!strcmp(owner, mrdp[i].mr_owner) &&
299		(mrdp[i].mr_flags & MDF_ACTIVE) &&
300		!(mrdp[i].mr_flags & MDF_FIXACTIVE)) {
301
302		mro.mo_desc = mrdp + i;
303		if (ioctl(memfd, MEMRANGE_SET, &mro))
304		    warn("couldn't clear range owned by '%s'", owner);
305	    }
306	}
307    } else if (mrd.mr_len != 0) {
308	/* clear-by-base/len */
309	mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
310	mro.mo_desc = &mrd;
311	if (ioctl(memfd, MEMRANGE_SET, &mro))
312	    err(1, "couldn't clear range");
313    } else {
314	help("clear");
315    }
316}
317
318static void
319helpfunc(__unused int memfd, __unused int argc, char *argv[])
320{
321    help(argv[1]);
322}
323
324static void
325help(const char *what)
326{
327    int		i;
328
329    if (what != NULL) {
330	/* find a function that matches */
331	for (i = 0; functions[i].cmd != NULL; i++)
332	    if (!strcmp(what, functions[i].cmd)) {
333		fprintf(stderr, "%s\n", functions[i].desc);
334		return;
335	    }
336	fprintf(stderr, "Unknown command '%s'\n", what);
337    }
338
339    /* print general help */
340    fprintf(stderr, "Valid commands are :\n");
341    for (i = 0; functions[i].cmd != NULL; i++)
342	fprintf(stderr, "    %s\n", functions[i].cmd);
343    fprintf(stderr, "Use help <command> for command-specific help\n");
344}
345