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