/*- * Copyright (c) 2008 Ariff Abdullah * 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 #include #include #include #include #include #include #include #include #include #include #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); }