/*-
* Copyright (c) 2008 Ariff Abdullah <ariff@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$
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <sysexits.h>
#include <stdint.h>
#include <cpu.h>
#define K8_MAXCPU 32 /* XXX whatever ... */
/*
* Please refer to:
*
* "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors"
* #32559 revision 3.00+.
*
* Our interest bits are C1eOnCmpHalt(28) and SmiOnCmpHalt(27), but the
* spec requires that IntrPndMsg(25) must be cleared if any of C1eOnCmpHalt
* or SmiOnCmpHalt being set to 1.
*/
#define K8_IP_MSR 0xc0010055 /* Interrupt Pending MSR */
#define K8_INTRPNDMSG_BIT 0x02000000ULL /* 1ULL << 25 : HT or IO */
#define K8_SMIONCMPHALT_BIT 0x08000000ULL /* 1ULL << 27 : SMI or NONE */
#define K8_C1EONCMPHALT_BIT 0x10000000ULL /* 1ULL << 28 : C1E or NONE */
/* Only valid for multi-cores k8 (and future) family */
#define K8_C1E_SUPPORTED(i) (((i) & 0x00000f00) == 0x00000f00 && \
((i) & 0x0fff0000) >= 0x00040000)
enum { K8_C1E_STATUS, K8_C1E_ENABLE, K8_C1E_DISABLE };
static int
usage(void)
{
fprintf(stderr, "usage: %s [enable | disable | status]\n",
getprogname());
return (EX_USAGE);
}
int
main(int argc, char **argv)
{
cpu_msr_args_t m;
cpu_cpuid_args_t id;
char cpubuf[16], vendor[13];
int i, fd, op;
if (argc != 2)
return (usage());
if (strcmp(argv[1], "enable") == 0)
op = K8_C1E_ENABLE;
else if (strcmp(argv[1], "disable") == 0)
op = K8_C1E_DISABLE;
else if (strcmp(argv[1], "status") == 0)
op = K8_C1E_STATUS;
else
return (usage());
m.msr = K8_IP_MSR;
for (i = 0; i < K8_MAXCPU; i++) {
bzero(cpubuf, sizeof(cpubuf));
bzero(vendor, sizeof(vendor));
snprintf(cpubuf, sizeof(cpubuf), "/dev/cpu%d", i);
fd = open(cpubuf, O_RDWR);
if (fd == -1) {
if (i == 0) {
warn("open(%s)", cpubuf);
return (EX_IOERR);
}
break;
}
id.level = 0;
if (ioctl(fd, CPU_CPUID, &id) == -1) {
close(fd);
continue;
}
((uint32_t *)vendor)[0] = id.data[1];
((uint32_t *)vendor)[1] = id.data[3];
((uint32_t *)vendor)[2] = id.data[2];
vendor[12] = '\0';
if (strcmp(vendor, AMD_VENDOR_ID) != 0) {
close(fd);
if (i == 0) {
warnx("CPU is not AMD");
return (EX_UNAVAILABLE);
}
continue;
}
id.level = 1;
if (ioctl(fd, CPU_CPUID, &id) == -1) {
close(fd);
continue;
}
if (!K8_C1E_SUPPORTED(id.data[0])) {
close(fd);
if (i == 0) {
warnx("C1E not supported");
return (EX_UNAVAILABLE);
}
continue;
}
if (ioctl(fd, CPU_RDMSR, &m) == -1) {
close(fd);
continue;
}
switch (op) {
case K8_C1E_ENABLE:
/*
* The spec requires that on setting C1eOnCmpHalt bit,
* both IntrPndMsg and SmiOnCmpHalt must be set to 0.
*/
m.data &= ~(K8_INTRPNDMSG_BIT | K8_SMIONCMPHALT_BIT);
m.data |= K8_C1EONCMPHALT_BIT;
case K8_C1E_DISABLE:
/*
* Both C1eOnCmpHalt and SmiOnCmpHalt must be set to 0.
* It seems that whatever previous value of IntrPndMsg
* has gone to oblivion...
*/
if (op == K8_C1E_DISABLE)
m.data &= ~(K8_C1EONCMPHALT_BIT |
K8_SMIONCMPHALT_BIT);
ioctl(fd, CPU_WRMSR, &m);
ioctl(fd, CPU_RDMSR, &m);
case K8_C1E_STATUS:
fprintf(stderr, "cpu%-2d : MSR=%#.16jx "
"C1E=%sabled, SMI=%sabled, INTR=%s\n",
i, (uintmax_t)m.data,
(m.data & K8_C1EONCMPHALT_BIT) ? "En" : "Dis",
(m.data & K8_SMIONCMPHALT_BIT) ? "En" : "Dis",
(m.data & K8_INTRPNDMSG_BIT) ? "HT" : "IO");
break;
default:
break;
}
close(fd);
}
return (EX_OK);
}