/*- * Copyright (c) 2007 Thomas Hurst * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #define K8TEMP_VERSION "0.3.0" /* * k8temp -- AMD K8 (AMD64, Opteron) on-die thermal sensor reader for FreeBSD. * * DragonFlyBSD should work fine, since it has pretty much the same /dev/pci. * OpenBSD should work with -DWITHOUT_PCIOCGETCONF using a kernel with USER_PCI. * NetBSD might work with -DWITH_LIBPCI, but it's only been vaguely build tested. */ #include #include #include #include #include #include #include #include "k8temp_pci.h" #include "k8temp.h" static int debug = 0; static void usage(int exit_code) { fprintf((exit_code == EX_OK ? stdout : stderr), "%s\n%s\n%s\n%s\n%s\n", "usage: k8temp [-nd] [cpu[:core[:sensor]] ...] | [-v | -h]", " -d Dump debugging info", " -h Display this help text", " -n Only display values", " -v Display version information"); exit(exit_code); } static void version(void) { printf("k8temp v%s\nCopyright 2007 Thomas Hurst \n", K8TEMP_VERSION); exit(EX_OK); } static void check_cpuid(void) { unsigned int vendor[3]; unsigned int maxeid,cpuid,pwrmgt,unused; int i; asm("cpuid": "=a" (unused), "=b" (vendor[0]), "=c" (vendor[2]), "=d" (vendor[1]) : "a" (0)); asm("cpuid": "=a" (cpuid), "=b" (unused), "=c" (unused), "=d" (unused) : "a" (1)); if (debug) fprintf(stderr, "CPUID: Vendor: %.12s, Id=0x%x Model=%d Family=%d Stepping=%d\n", (char *)vendor, cpuid, (cpuid >> 4) & 0xf, (cpuid >> 8) & 0xf, cpuid & 0xf); if (0 != memcmp((char *)&vendor, "AuthenticAMD", (size_t) 12)) errx(EX_UNAVAILABLE, "Only AMD CPU's are supported by k8temp"); asm("cpuid": "=a" (maxeid), "=b" (unused), "=c" (unused), "=d" (unused) : "a" (CPUID_EXTENDED)); if (maxeid >= CPUID_POWERMGT) { asm("cpuid": "=a" (unused), "=b" (unused), "=c" (unused), "=d" (pwrmgt) : "a" (CPUID_POWERMGT)); if (debug) { /* overkill \o/ */ fprintf(stderr, "Advanced Power Management=0x%x\n", pwrmgt); i = 0; do { fprintf(stderr, " %20s: %s\n", advPwrMgtFlags[i], (pwrmgt >> i) & 1 ? "Yes" : "No"); } while (advPwrMgtFlags[++i]); } if (!(pwrmgt & 1)) errx(EX_UNAVAILABLE, "Thermal sensor support not reported in CPUID"); } else errx(EX_UNAVAILABLE, "CPU lacks Advanced Power Management support"); /* Linux only checks these 3 */ if (cpuid == 0xf40 || cpuid == 0xf50 || cpuid == 0xf51) errx(EX_UNAVAILABLE, "This CPU stepping does not support thermal sensors."); } static int get_temp(k8_pcidev dev, int core, int sensor) { static int thermtp = 0; uint32_t ctrl,therm; if (!k8_pci_read_byte(dev, THERM_REG, &ctrl)) return(TEMP_ERR); if (core == 1) /* CPU0 has the bit set according to datasheet */ ctrl &= ~SEL_CORE; else if (core == 0) ctrl |= SEL_CORE; else return(TEMP_ERR); if (sensor == 0) ctrl &= ~SEL_SENSOR; else if (sensor == 1) ctrl |= SEL_SENSOR; else return(TEMP_ERR); if (!k8_pci_write_byte(dev, THERM_REG, ctrl)) return(TEMP_ERR); if (!k8_pci_read_word(dev, THERM_REG, &therm)) return(TEMP_ERR); /* verify the selection took */ if ((ctrl & (SEL_CORE|SEL_SENSOR)) != (therm & (SEL_CORE|SEL_SENSOR))) return(TEMP_ERR); if (THERMTRIP(therm) && !thermtp) { fprintf(stderr, "Thermal trip bit set, system overheating?\n"); thermtp = 1; } if (debug) fprintf(stderr, "Thermtrip=0x%08x (CurTmp=0x%02x (%dc) TjOffset=0x%02x DiodeOffset=0x%02x (%dc))\n", therm, CURTMP(therm), CURTMP(therm) + TEMP_MIN, TJOFFSET(therm), DIODEOFFSET(therm), OFFSET_MAX - DIODEOFFSET(therm)); return(CURTMP(therm) + TEMP_MIN); } int main(int argc, char *argv[]) { k8_pcidev devs[MAX_CPU]; unsigned int cpucount,cpu,core,sensor; int temp; int exit_code = EX_UNAVAILABLE; int opt; int value_only = 0; int i; char selections[MAX_CPU][MAX_CORE][MAX_SENSOR]; int selcount,selected = 0; while ((opt = getopt(argc, argv, "dhnv")) != -1) switch (opt) { case 'd': debug = 1; break; case 'n': value_only = 1; break; case 'v': version(); case 'h': usage(EX_OK); default: usage(EX_USAGE); } bzero(&selections, sizeof(selections)); for (i = optind; i < argc; i++) { selcount = sscanf(argv[i], "%u:%u:%u", &cpu, &core, &sensor); selected += selcount; if (selcount > 0 && cpu >= MAX_CPU) errx(EX_USAGE, "CPU selector %d out of range 0-%d", cpu, MAX_CPU - 1); if (selcount > 1 && core >= MAX_CORE) errx(EX_USAGE, "Core selector %d out of range 0-%d", cpu, MAX_CORE - 1); if (selcount > 2 && sensor >= MAX_SENSOR) errx(EX_USAGE, "Sensor selector %d out of range 0-%d", cpu, MAX_SENSOR - 1); switch (selcount) { case 1: memset(selections[cpu], 1, sizeof(selections[cpu])); break; case 2: memset(selections[cpu][core], 1, sizeof(selections[cpu][core])); break; case 3: selections[cpu][core][sensor] = 1; break; case 0: default: warnx("Illegal selector: %s", argv[i]); usage(EX_USAGE); } } check_cpuid(); k8_pci_init(); cpu = 0; cpucount = k8_pci_vendor_device_list(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_MISC_CTRL, devs, MAX_CPU); for (cpu=0; cpu <= cpucount; cpu++) { for (core = 0; core < MAX_CORE; core++) { for (sensor = 0; sensor < MAX_SENSOR; sensor++) { if (selected > 0 && !selections[cpu][core][sensor]) continue; temp = get_temp(devs[cpu], core, sensor); if (temp > TEMP_MIN) { if (value_only) printf("%d\n", temp); else printf("CPU %d Core %d Sensor %d: %dc\n", cpu, core, sensor, temp); exit_code = EX_OK; } } } } k8_pci_close(); exit(exit_code); }