memcontrol.c revision 103346
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 103346 2002-09-15 15:07:55Z dwmalone $
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 <paths.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41struct
42{
43    char	*name;
44    int		val;
45    int		kind;
46#define MDF_SETTABLE	(1<<0)
47} attrnames[] = {
48    {"uncacheable",	MDF_UNCACHEABLE,	MDF_SETTABLE},
49    {"write-combine",	MDF_WRITECOMBINE,	MDF_SETTABLE},
50    {"write-through",	MDF_WRITETHROUGH,	MDF_SETTABLE},
51    {"write-back",	MDF_WRITEBACK,		MDF_SETTABLE},
52    {"write-protect",	MDF_WRITEPROTECT,	MDF_SETTABLE},
53    {"force",		MDF_FORCE,		MDF_SETTABLE},
54    {"unknown",		MDF_UNKNOWN,		0},
55    {"fixed-base",	MDF_FIXBASE,		0},
56    {"fixed-length",	MDF_FIXLEN,		0},
57    {"set-by-firmware",	MDF_FIRMWARE,		0},
58    {"active",		MDF_ACTIVE,		MDF_SETTABLE},
59    {"bogus",		MDF_BOGUS,		0},
60    {NULL,		0,			0}
61};
62
63static void	listfunc(int memfd, int argc, char *argv[]);
64static void	setfunc(int memfd, int argc, char *argv[]);
65static void	clearfunc(int memfd, int argc, char *argv[]);
66static void	helpfunc(int memfd, int argc, char *argv[]);
67static void	help(char *what);
68
69struct
70{
71    char	*cmd;
72    char	*desc;
73    void	(*func)(int memfd, int argc, char *argv[]);
74} functions[] = {
75    {"list",
76     "List current memory range attributes\n"
77     "    list [-a]\n"
78     "        -a    list all range slots, even those that are inactive",
79     listfunc},
80    {"set",
81     "Set memory range attributes\n"
82     "    set -b <base> -l <length> -o <owner> <attribute>\n"
83     "        <base>      memory range base address\n"
84     "        <length>    length of memory range in bytes, power of 2\n"
85     "        <owner>     text identifier for this setting (7 char max)\n"
86     "        <attribute> attribute(s) to be applied to this range:\n"
87     "                        uncacheable\n"
88     "                        write-combine\n"
89     "                        write-through\n"
90     "                        write-back\n"
91     "                        write-protect",
92     setfunc},
93    {"clear",
94     "Clear memory range attributes\n"
95     "    clear -o <owner>\n"
96     "        <owner>     all ranges with this owner will be cleared\n"
97     "    clear -b <base> -l <length>\n"
98     "        <base>      memory range base address\n"
99     "        <length>    length of memory range in bytes, power of 2\n"
100     "                    Base and length must exactly match an existing range",
101     clearfunc},
102    {NULL,	NULL,					helpfunc}
103};
104
105int
106main(int argc, char *argv[])
107{
108    int		i, memfd;
109
110    if (argc < 2) {
111	help(NULL);
112    } else {
113	if ((memfd = open(_PATH_MEM, O_RDONLY)) == -1)
114	    err(1, "can't open %s", _PATH_MEM);
115
116	for (i = 0; functions[i].cmd != NULL; i++)
117	    if (!strcmp(argv[1], functions[i].cmd))
118		break;
119	functions[i].func(memfd, argc - 1, argv + 1);
120	close(memfd);
121    }
122    return(0);
123}
124
125static struct mem_range_desc *
126mrgetall(int memfd, int *nmr)
127{
128    struct mem_range_desc	*mrd;
129    struct mem_range_op		mro;
130
131    mro.mo_arg[0] = 0;
132    if (ioctl(memfd, MEMRANGE_GET, &mro))
133	err(1, "can't size range descriptor array");
134
135    *nmr = mro.mo_arg[0];
136    mrd = malloc(*nmr * sizeof(struct mem_range_desc));
137    if (mrd == NULL)
138	errx(1, "can't allocate %d bytes for %d range descriptors",
139	     *nmr * sizeof(struct mem_range_desc), *nmr);
140
141    mro.mo_arg[0] = *nmr;
142    mro.mo_desc = mrd;
143    if (ioctl(memfd, MEMRANGE_GET, &mro))
144	err(1, "can't fetch range descriptor array");
145
146    return(mrd);
147}
148
149
150static void
151listfunc(int memfd, int argc, char *argv[])
152{
153    struct mem_range_desc	*mrd;
154    int				nd, i, j;
155    int				ch;
156    int				showall = 0;
157    char			*owner;
158
159    owner = NULL;
160    while ((ch = getopt(argc, argv, "ao:")) != -1)
161	switch(ch) {
162	case 'a':
163	    showall = 1;
164	    break;
165	case 'o':
166	    owner = strdup(optarg);
167	    break;
168	case '?':
169	default:
170	    help("list");
171	}
172
173    mrd = mrgetall(memfd, &nd);
174
175    for (i = 0; i < nd; i++) {
176	if (!showall && !(mrd[i].mr_flags & MDF_ACTIVE))
177	    continue;
178	if (owner && strcmp(mrd[i].mr_owner, owner))
179	    continue;
180	printf("%qx/%qx %.8s ", mrd[i].mr_base, mrd[i].mr_len,
181	       mrd[i].mr_owner[0] ? mrd[i].mr_owner : "-");
182	for (j = 0; attrnames[j].name != NULL; j++)
183	    if (mrd[i].mr_flags & attrnames[j].val)
184		printf("%s ", attrnames[j].name);
185	printf("\n");
186    }
187    free(mrd);
188    if (owner)
189	free(owner);
190}
191
192static void
193setfunc(int memfd, int argc, char *argv[])
194{
195    struct mem_range_desc	mrd;
196    struct mem_range_op		mro;
197    int				i;
198    int				ch;
199    char			*ep;
200
201    mrd.mr_base = 0;
202    mrd.mr_len = 0;
203    mrd.mr_flags = 0;
204    strcpy(mrd.mr_owner, "user");
205    while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
206	switch(ch) {
207	case 'b':
208	    mrd.mr_base = strtouq(optarg, &ep, 0);
209	    if ((ep == optarg) || (*ep != 0))
210		help("set");
211	    break;
212	case 'l':
213	    mrd.mr_len = strtouq(optarg, &ep, 0);
214	    if ((ep == optarg) || (*ep != 0))
215		help("set");
216	    break;
217	case 'o':
218	    if ((*optarg == 0) || (strlen(optarg) > 7))
219		help("set");
220	    strcpy(mrd.mr_owner, optarg);
221	    break;
222
223	case '?':
224	default:
225	    help("set");
226	}
227
228    if (mrd.mr_len == 0)
229	help("set");
230
231    argc -= optind;
232    argv += optind;
233
234    while(argc--) {
235	for (i = 0; attrnames[i].name != NULL; i++) {
236	    if (!strcmp(attrnames[i].name, argv[0])) {
237		if (!attrnames[i].kind & MDF_SETTABLE)
238		    help("flags");
239		mrd.mr_flags |= attrnames[i].val;
240		break;
241	    }
242	}
243	if (attrnames[i].name == NULL)
244	    help("flags");
245	argv++;
246    }
247
248    mro.mo_desc = &mrd;
249    mro.mo_arg[0] = 0;
250    if (ioctl(memfd, MEMRANGE_SET, &mro))
251	err(1, "can't set range");
252}
253
254static void
255clearfunc(int memfd, int argc, char *argv[])
256{
257    struct mem_range_desc	mrd, *mrdp;
258    struct mem_range_op		mro;
259    int				i, nd;
260    int				ch;
261    char			*ep, *owner;
262
263    mrd.mr_base = 0;
264    mrd.mr_len = 0;
265    owner = NULL;
266    while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
267	switch(ch) {
268	case 'b':
269	    mrd.mr_base = strtouq(optarg, &ep, 0);
270	    if ((ep == optarg) || (*ep != 0))
271		help("clear");
272	    break;
273	case 'l':
274	    mrd.mr_len = strtouq(optarg, &ep, 0);
275	    if ((ep == optarg) || (*ep != 0))
276		help("clear");
277	    break;
278	case 'o':
279	    if ((*optarg == 0) || (strlen(optarg) > 7))
280		help("clear");
281	    owner = strdup(optarg);
282	    break;
283
284	case '?':
285	default:
286	    help("clear");
287	}
288
289    if (owner != NULL) {
290	/* clear-by-owner */
291	if ((mrd.mr_base != 0) || (mrd.mr_len != 0))
292	    help("clear");
293
294	mrdp = mrgetall(memfd, &nd);
295	mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
296	for (i = 0; i < nd; i++) {
297	    if (!strcmp(owner, mrdp[i].mr_owner) &&
298		(mrdp[i].mr_flags & MDF_ACTIVE) &&
299		!(mrdp[i].mr_flags & MDF_FIXACTIVE)) {
300
301		mro.mo_desc = mrdp + i;
302		if (ioctl(memfd, MEMRANGE_SET, &mro))
303		    warn("couldn't clear range owned by '%s'", owner);
304	    }
305	}
306    } else if (mrd.mr_len != 0) {
307	/* clear-by-base/len */
308	mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
309	mro.mo_desc = &mrd;
310	if (ioctl(memfd, MEMRANGE_SET, &mro))
311	    err(1, "couldn't clear range");
312    } else {
313	help("clear");
314    }
315}
316
317static void
318helpfunc(int memfd, int argc, char *argv[])
319{
320    help(argv[1]);
321}
322
323static void
324help(char *what)
325{
326    int		i;
327
328    if (what != NULL) {
329	/* find a function that matches */
330	for (i = 0; functions[i].cmd != NULL; i++)
331	    if (!strcmp(what, functions[i].cmd)) {
332		fprintf(stderr, "%s\n", functions[i].desc);
333		return;
334	    }
335	fprintf(stderr, "Unknown command '%s'\n", what);
336    }
337
338    /* print general help */
339    fprintf(stderr, "Valid commands are :\n");
340    for (i = 0; functions[i].cmd != NULL; i++)
341	fprintf(stderr, "    %s\n", functions[i].cmd);
342    fprintf(stderr, "Use help <command> for command-specific help\n");
343}
344