[BACK]Return to memconfig.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / memconfig

File: [local] / src / usr.sbin / memconfig / memconfig.c (download)

Revision 1.19, Fri Jun 28 13:32:48 2019 UTC (4 years, 11 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, HEAD
Changes since 1.18: +6 -6 lines

When system calls indicate an error they return -1, not some arbitrary
value < 0.  errno is only updated in this case.  Change all (most?)
callers of syscalls to follow this better, and let's see if this strictness
helps us in the future.

/* $OpenBSD: memconfig.c,v 1.19 2019/06/28 13:32:48 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},
	{"fix-active",		MDF_FIXACTIVE,		0},
	{"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 = -1;

	if (argc < 2) {
		help(NULL);
	} else {
		for (i = 0; functions[i].cmd != NULL; i++)
			if (!strcmp(argv[1], functions[i].cmd))
				break;
		
		if ((functions[i].func != helpfunc) &&
		    (memfd = open("/dev/mem", O_RDONLY)) == -1)
			err(1, "can't open /dev/mem");
		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) == -1)
		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) == -1)
		err(1, "can't fetch range descriptor array");

	return(mrd);
}


static void
listfunc(int memfd, int argc, char *argv[])
{
	int	nd, i, j, k, 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("%llx/%llx %.8s ", mrd[i].mr_base, mrd[i].mr_len,
		       mrd[i].mr_owner[0] ? mrd[i].mr_owner : "-");
		for (j = 0; j < 32; j++) {
			if ( ((1<<j) & mrd[i].mr_flags) == 0)
				continue;
			for (k = 0; attrnames[k].name != NULL; k++)
				if (((1<<j) & mrd[i].mr_flags) & attrnames[k].val) {
					printf("%s ", attrnames[k].name);
					break;
				}
			if (attrnames[k].name == NULL)
				printf("0x%x", (1<<j) & mrd[i].mr_flags);
		}
		printf("\n");
	}
	free(mrd);
	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 = strtoull(optarg, &ep, 0);
			if ((ep == optarg) || (*ep != 0))
				help("set");
			break;
		case 'l':
			mrd.mr_len = strtoull(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) == -1)
		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, got_base = 0;
	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 = strtoull(optarg, &ep, 0);
			if ((ep == optarg) || (*ep != 0))
				help("clear");
			else
				got_base = 1;
			break;
		case 'l':
			mrd.mr_len = strtoull(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 (got_base || 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) == -1)
					warn("couldn't clear range owned by '%s'",
					    owner);
			}
		}
	} else if (got_base && 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) == -1)
			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");
}