xref: /freebsd/usr.sbin/memcontrol/memcontrol.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
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 <paths.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 struct
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     {"fixed-base",	MDF_FIXBASE,		0},
54     {"fixed-length",	MDF_FIXLEN,		0},
55     {"set-by-firmware",	MDF_FIRMWARE,		0},
56     {"active",		MDF_ACTIVE,		MDF_SETTABLE},
57     {"bogus",		MDF_BOGUS,		0},
58     {NULL,		0,			0}
59 };
60 
61 static void	listfunc(int memfd, int argc, char *argv[]);
62 static void	setfunc(int memfd, int argc, char *argv[]);
63 static void	clearfunc(int memfd, int argc, char *argv[]);
64 static void	helpfunc(int memfd, int argc, char *argv[]);
65 static void	help(char *what);
66 
67 struct
68 {
69     char	*cmd;
70     char	*desc;
71     void	(*func)(int memfd, int argc, char *argv[]);
72 } functions[] = {
73     {"list",
74      "List current memory range attributes\n"
75      "    list [-a]\n"
76      "        -a    list all range slots, even those that are inactive",
77      listfunc},
78     {"set",
79      "Set memory range attributes\n"
80      "    set -b <base> -l <length> -o <owner> <attribute>\n"
81      "        <base>      memory range base address\n"
82      "        <length>    length of memory range in bytes, power of 2\n"
83      "        <owner>     text identifier for this setting (7 char max)\n"
84      "        <attribute> attribute(s) to be applied to this range:\n"
85      "                        uncacheable\n"
86      "                        write-combine\n"
87      "                        write-through\n"
88      "                        write-back\n"
89      "                        write-protect",
90      setfunc},
91     {"clear",
92      "Clear memory range attributes\n"
93      "    clear -o <owner>\n"
94      "        <owner>     all ranges with this owner will be cleared\n"
95      "    clear -b <base> -l <length>\n"
96      "        <base>      memory range base address\n"
97      "        <length>    length of memory range in bytes, power of 2\n"
98      "                    Base and length must exactly match an existing range",
99      clearfunc},
100     {NULL,	NULL,					helpfunc}
101 };
102 
103 int
104 main(int argc, char *argv[])
105 {
106     int		i, memfd;
107 
108     if (argc < 2) {
109 	help(NULL);
110     } else {
111 	if ((memfd = open(_PATH_MEM, O_RDONLY)) == -1)
112 	    err(1, "can't open %s", _PATH_MEM);
113 
114 	for (i = 0; functions[i].cmd != NULL; i++)
115 	    if (!strcmp(argv[1], functions[i].cmd))
116 		break;
117 	functions[i].func(memfd, argc - 1, argv + 1);
118 	close(memfd);
119     }
120     return(0);
121 }
122 
123 static struct mem_range_desc *
124 mrgetall(int memfd, int *nmr)
125 {
126     struct mem_range_desc	*mrd;
127     struct mem_range_op		mro;
128 
129     mro.mo_arg[0] = 0;
130     if (ioctl(memfd, MEMRANGE_GET, &mro))
131 	err(1, "can't size range descriptor array");
132 
133     *nmr = mro.mo_arg[0];
134     mrd = malloc(*nmr * sizeof(struct mem_range_desc));
135     if (mrd == NULL)
136 	errx(1, "can't allocate %d bytes for %d range descriptors",
137 	     *nmr * sizeof(struct mem_range_desc), *nmr);
138 
139     mro.mo_arg[0] = *nmr;
140     mro.mo_desc = mrd;
141     if (ioctl(memfd, MEMRANGE_GET, &mro))
142 	err(1, "can't fetch range descriptor array");
143 
144     return(mrd);
145 }
146 
147 
148 static void
149 listfunc(int memfd, int argc, char *argv[])
150 {
151     struct mem_range_desc	*mrd;
152     int				nd, i, j;
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_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