OpenBSD-4.6/usr.sbin/memconfig/memconfig.c

Compare this file to the similar file:
Show the results in this format:

/* $OpenBSD: memconfig.c,v 1.12 2007/09/02 15:19:39 deraadt Exp $ */

/*-
 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: /home/ncvs/src/usr.sbin/memcontrol/memcontrol.c,v 1.8 2002/09/15 15:07:55 dwmalone Exp $
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/memrange.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct {
	const char	*name;
	int		val;
	int		kind;
#define MDF_SETTABLE	(1<<0)
} attrnames[] = {
	{"uncacheable",		MDF_UNCACHEABLE,	MDF_SETTABLE},
	{"write-combine",	MDF_WRITECOMBINE,	MDF_SETTABLE},
	{"write-through",	MDF_WRITETHROUGH,	MDF_SETTABLE},
	{"write-back",		MDF_WRITEBACK,		MDF_SETTABLE},
	{"write-protect",	MDF_WRITEPROTECT,	MDF_SETTABLE},
	{"force",		MDF_FORCE,		MDF_SETTABLE},
	{"unknown",		MDF_UNKNOWN,		0},
	{"fixed-base",		MDF_FIXBASE,		0},
	{"fixed-length",	MDF_FIXLEN,		0},
	{"set-by-firmware",	MDF_FIRMWARE,		0},
	{"active",		MDF_ACTIVE,		MDF_SETTABLE},
	{"bogus",		MDF_BOGUS,		0},
	{NULL,			0,			0}
};

static void	listfunc(int, int, char *[]);
static void	setfunc(int, int, char *[]);
static void	clearfunc(int, int, char *[]);
static void	helpfunc(int, int, char *[]);
static void	help(const char *);
static struct mem_range_desc *mrgetall(int, int *);

struct
{
	const char	*cmd;
	const char	*desc;
	void	(*func)(int, int, char *[]);
} functions[] = {
	{"list",
	 "List current memory range attributes\n"
	 "    list [-a]\n"
	 "        -a    list all range slots, even those that are inactive",
	 listfunc},
	{"set",
	 "Set memory range attributes\n"
	 "    set -b <base> -l <length> -o <owner> <attribute>\n"
	 "        <base>      memory range base address\n"
	 "        <length>    length of memory range in bytes, power of 2\n"
	 "        <owner>     text identifier for this setting (7 char max)\n"
	 "        <attribute> attribute(s) to be applied to this range:\n"
	 "                        uncacheable\n"
	 "                        write-combine\n"
	 "                        write-through\n"
	 "                        write-back\n"
	 "                        write-protect",
	 setfunc},
	{"clear",
	 "Clear memory range attributes\n"
	 "    clear -o <owner>\n"
	 "        <owner>     all ranges with this owner will be cleared\n"
	 "    clear -b <base> -l <length>\n"
	 "        <base>      memory range base address\n"
	 "        <length>    length of memory range in bytes, power of 2\n"
	 "                    Base and length must exactly match an existing range",
	 clearfunc},
	{NULL,	NULL,					helpfunc}
};

int
main(int argc, char *argv[])
{
	int	 i, memfd;

	if (argc < 2) {
		help(NULL);
	} else {
		if ((memfd = open("/dev/mem", O_RDONLY)) == -1)
			err(1, "can't open /dev/mem");

		for (i = 0; functions[i].cmd != NULL; i++)
			if (!strcmp(argv[1], functions[i].cmd))
				break;
		functions[i].func(memfd, argc - 1, argv + 1);
		close(memfd);
	}
	return(0);
}

static struct mem_range_desc *
mrgetall(int memfd, int *nmr)
{
	struct mem_range_desc	*mrd;
	struct mem_range_op	mro;

	mro.mo_arg[0] = 0;
	if (ioctl(memfd, MEMRANGE_GET, &mro))
		err(1, "can't size range descriptor array");

	*nmr = mro.mo_arg[0];
	mrd = calloc(*nmr, sizeof(struct mem_range_desc));
	if (mrd == NULL)
		errx(1, "can't allocate %zu bytes for %d range descriptors",
		     *nmr * sizeof(struct mem_range_desc), *nmr);

	mro.mo_arg[0] = *nmr;
	mro.mo_desc = mrd;
	if (ioctl(memfd, MEMRANGE_GET, &mro))
		err(1, "can't fetch range descriptor array");

	return(mrd);
}


static void
listfunc(int memfd, int argc, char *argv[])
{
	int	nd, i, j, ch, showall = 0;
	struct mem_range_desc	*mrd;
	char	*owner;

	owner = NULL;
	while ((ch = getopt(argc, argv, "ao:")) != -1)
		switch(ch) {
		case 'a':
			showall = 1;
			break;
		case 'o':
			if (!(owner = strdup(optarg)))
				errx(1, "out of memory");
			break;
		default:
			help("list");
		}

	mrd = mrgetall(memfd, &nd);

	for (i = 0; i < nd; i++) {
		if (!showall && !(mrd[i].mr_flags & MDF_ACTIVE))
			continue;
		if (owner && strcmp(mrd[i].mr_owner, owner))
			continue;
		printf("%qx/%qx %.8s ", mrd[i].mr_base, mrd[i].mr_len,
		       mrd[i].mr_owner[0] ? mrd[i].mr_owner : "-");
		for (j = 0; attrnames[j].name != NULL; j++)
			if (mrd[i].mr_flags & attrnames[j].val)
				printf("%s ", attrnames[j].name);
		printf("\n");
	}
	free(mrd);
	if (owner)
		free(owner);
}

static void
setfunc(int memfd, int argc, char *argv[])
{
	struct mem_range_desc	 mrd;
	struct mem_range_op	 mro;
	int	 i, ch;
	char	*ep;

	mrd.mr_base = 0;
	mrd.mr_len = 0;
	mrd.mr_flags = 0;
	strlcpy(mrd.mr_owner, "user", sizeof mrd.mr_owner);

	while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
		switch(ch) {
		case 'b':
			mrd.mr_base = strtouq(optarg, &ep, 0);
			if ((ep == optarg) || (*ep != 0))
				help("set");
			break;
		case 'l':
			mrd.mr_len = strtouq(optarg, &ep, 0);
			if ((ep == optarg) || (*ep != 0))
				help("set");
			break;
		case 'o':
			if (*optarg == 0 ||
			    strlen(optarg) > sizeof(mrd.mr_owner)-1)
				help("set");
			strlcpy(mrd.mr_owner, optarg, sizeof mrd.mr_owner);
			break;
		default:
			help("set");
		}

	if (mrd.mr_len == 0)
		help("set");

	argc -= optind;
	argv += optind;

	while(argc--) {
		for (i = 0; attrnames[i].name != NULL; i++) {
			if (!strcmp(attrnames[i].name, argv[0])) {
				if (!attrnames[i].kind & MDF_SETTABLE)
					help("flags");
				mrd.mr_flags |= attrnames[i].val;
				break;
			}
		}
		if (attrnames[i].name == NULL)
			help("flags");
		argv++;
	}

	mro.mo_desc = &mrd;
	mro.mo_arg[0] = 0;
	if (ioctl(memfd, MEMRANGE_SET, &mro))
		err(1, "can't set range");
}

static void
clearfunc(int memfd, int argc, char *argv[])
{
	struct mem_range_desc	 mrd, *mrdp;
	struct mem_range_op      mro;
	int	i, nd, ch;
	char	*ep, *owner;

	mrd.mr_base = 0;
	mrd.mr_len = 0;
	owner = NULL;
	while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
		switch(ch) {
		case 'b':
			mrd.mr_base = strtouq(optarg, &ep, 0);
			if ((ep == optarg) || (*ep != 0))
				help("clear");
			break;
		case 'l':
			mrd.mr_len = strtouq(optarg, &ep, 0);
			if ((ep == optarg) || (*ep != 0))
				help("clear");
			break;
		case 'o':
			if ((*optarg == 0) || (strlen(optarg) > 7))
				help("clear");
			if (!(owner = strdup(optarg)))
				errx(1, "out of memory");
			break;

		default:
			help("clear");
		}

	if (owner != NULL) {
		/* clear-by-owner */
		if ((mrd.mr_base != 0) || (mrd.mr_len != 0))
			help("clear");

		mrdp = mrgetall(memfd, &nd);
		mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
		for (i = 0; i < nd; i++) {
			if (!strcmp(owner, mrdp[i].mr_owner) &&
			    (mrdp[i].mr_flags & MDF_ACTIVE) &&
			    !(mrdp[i].mr_flags & MDF_FIXACTIVE)) {

				mro.mo_desc = mrdp + i;
				if (ioctl(memfd, MEMRANGE_SET, &mro))
					warn("couldn't clear range owned by '%s'",
					    owner);
			}
		}
	} else if ((mrd.mr_base != 0) && (mrd.mr_len != 0)) {
		/* clear-by-base/len */
		mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
		mro.mo_desc = &mrd;
		if (ioctl(memfd, MEMRANGE_SET, &mro))
			err(1, "couldn't clear range");
	} else {
		help("clear");
	}
}

static void
helpfunc(int memfd, int argc, char *argv[])
{
	help(argv[1]);
}

static void
help(const char *what)
{
	int	 i;

	if (what != NULL) {
		/* find a function that matches */
		for (i = 0; functions[i].cmd != NULL; i++)
			if (!strcmp(what, functions[i].cmd)) {
				fprintf(stderr, "%s\n", functions[i].desc);
				return;
			}
		fprintf(stderr, "Unknown command '%s'\n", what);
	}

	/* print general help */
	fprintf(stderr, "Valid commands are :\n");
	for (i = 0; functions[i].cmd != NULL; i++)
		fprintf(stderr, "    %s\n", functions[i].cmd);
	fprintf(stderr, "Use help <command> for command-specific help\n");
}