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