diff --git a/app/main.c b/app/main.c index 19098e6..734b0e7 100644 --- a/app/main.c +++ b/app/main.c @@ -251,6 +251,8 @@ static void global_init(void) error_init(); + temperature_init(); + initial_config(); clear_message_area(); diff --git a/system/cpuid.c b/system/cpuid.c index 1ca7eca..8217ee9 100644 --- a/system/cpuid.c +++ b/system/cpuid.c @@ -36,7 +36,7 @@ void cpuid_init(void) // Get the processor family information & feature flags. if (cpuid_info.max_cpuid >= 1) { cpuid(0x1, 0, - &cpuid_info.version.raw, + &cpuid_info.version.raw[0], &cpuid_info.proc_info.raw, &cpuid_info.flags.raw[1], &cpuid_info.flags.raw[0] @@ -65,8 +65,8 @@ void cpuid_init(void) if (cpuid_info.max_xcpuid >= 0x80000001) { cpuid(0x80000001, 0, ®[0], + &cpuid_info.version.raw[1], ®[1], - ®[2], &cpuid_info.flags.raw[2] ); } diff --git a/system/cpuid.h b/system/cpuid.h index 009772f..c84fd27 100644 --- a/system/cpuid.h +++ b/system/cpuid.h @@ -29,7 +29,7 @@ typedef enum { */ typedef union { - uint32_t raw; + uint32_t raw[2]; struct { uint32_t stepping : 4; uint32_t model : 4; @@ -39,6 +39,7 @@ typedef union { uint32_t extendedModel : 4; uint32_t extendedFamily : 8; uint32_t : 4; + uint32_t extendedBrandID : 32; // AMD Only }; } cpuid_version_t; diff --git a/system/hwquirks.c b/system/hwquirks.c index dbff37b..f1fd0ed 100644 --- a/system/hwquirks.c +++ b/system/hwquirks.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2004-2022 Samuel Demeulemeester +// Copyright (C) 2004-2023 Sam Demeulemeester // // ------------------------ // This file is used to detect quirks on specific hardware @@ -13,6 +13,9 @@ #include "pci.h" #include "unistd.h" #include "cpuinfo.h" +#include "cpuid.h" +#include "config.h" +#include "temperature.h" quirk_t quirk; @@ -64,6 +67,38 @@ static void get_m1541_l2_cache_size(void) if (reg == 0b10) { l2_cache = 1024; } } +static void disable_temp_reporting(void) +{ + enable_temperature = false; +} + +static void amd_k8_revfg_temp(void) +{ + uint32_t rtcr = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K8); + + // For Rev F & G, switch sensor if no temperature is reported + if (!((rtcr >> 16) & 0xFF)) { + pci_config_write8(0, 24, 3, AMD_TEMP_REG_K8, rtcr | 0x04); + } + + // K8 Rev G Desktop requires an additional offset. + if (cpuid_info.version.extendedModel < 6 && cpuid_info.version.extendedModel > 7) // Not Rev G + return; + + if (cpuid_info.version.extendedModel == 6 && cpuid_info.version.extendedModel < 9) // Not Desktop + return; + + uint16_t brandID = (cpuid_info.version.extendedBrandID >> 9) & 0x1f; + + if (cpuid_info.version.model == 0xF && (brandID == 0x7 || brandID == 0x9 || brandID == 0xC)) // Mobile (Single Core) + return; + + if (cpuid_info.version.model == 0xB && brandID > 0xB) // Mobile (Dual Core) + return; + + cpu_temp_offset = 21.0f; +} + // --------------------- // -- Public function -- // --------------------- @@ -115,4 +150,53 @@ void quirks_init(void) quirk.process = NULL; } } + + // ------------------------------------------------------ + // -- Early AMD K8 doesn't support temperature reading -- + // ------------------------------------------------------ + // The on-die temperature diode on SH-B0/B3 stepping does not work. + if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF + && cpuid_info.version.extendedFamily == 0 && cpuid_info.version.extendedModel == 0) { // Early K8 + if ((cpuid_info.version.model == 4 && cpuid_info.version.stepping == 0) || // SH-B0 ClawHammer (Athlon 64) + (cpuid_info.version.model == 5 && cpuid_info.version.stepping <= 1)) { // SH-B0/B3 SledgeHammer (Opteron) + quirk.id = QUIRK_K8_BSTEP_NOTEMP; + quirk.type |= QUIRK_TYPE_TEMP; + quirk.process = disable_temp_reporting; + } + } + + // --------------------------------------------------- + // -- Late AMD K8 (rev F/G) temp sensor workaround -- + // --------------------------------------------------- + if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF + && cpuid_info.version.extendedFamily == 0 && cpuid_info.version.extendedModel >= 4) { // Later K8 + + quirk.id = QUIRK_K8_REVFG_TEMP; + quirk.type |= QUIRK_TYPE_TEMP; + quirk.process = amd_k8_revfg_temp; + } + + // ------------------------------------------------ + // -- AMD K10 CPUs Temp workaround (Errata #319) -- + // ------------------------------------------------ + // Some AMD K10 CPUs on Socket AM2+/F have buggued thermal diode leading + // to inaccurate temperature measurements. Affected steppings: DR-BA/B2/B3, RB-C2 & HY-D0. + if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF + && cpuid_info.version.extendedFamily == 1 && cpuid_info.version.extendedModel == 0) { // AMD K10 + + uint8_t pkg_type = (cpuid_info.version.extendedBrandID >> 28) & 0x0F; + uint32_t dct0_high = pci_config_read32(0, 24, 2, 0x94); // 0x94[8] = 1 for DDR3 + + if (pkg_type == 0b0000 || (pkg_type == 0b0001 && (((dct0_high >> 8) & 1) == 0))) { // Socket F or AM2+ (exclude AM3) + + if (cpuid_info.version.model < 4 || // DR-BA, DR-B2 & DR-B3 + (cpuid_info.version.model == 4 && cpuid_info.version.stepping <= 2) || // RB-C2 + cpuid_info.version.model == 8) { // HY-D0 + + quirk.id = QUIRK_AMD_ERRATA_319; + quirk.type |= QUIRK_TYPE_TEMP; + quirk.process = disable_temp_reporting; + } + } + } } diff --git a/system/hwquirks.h b/system/hwquirks.h index 6005611..daf22b8 100644 --- a/system/hwquirks.h +++ b/system/hwquirks.h @@ -19,12 +19,16 @@ #define QUIRK_TYPE_SMBUS (1 << 4) #define QUIRK_TYPE_TIMER (1 << 5) #define QUIRK_TYPE_MEM_SIZE (1 << 6) +#define QUIRK_TYPE_TEMP (1 << 7) typedef enum { QUIRK_NONE, QUIRK_TUSL2, QUIRK_ALI_ALADDIN_V, - QUIRK_X10SDV_NOSMP + QUIRK_X10SDV_NOSMP, + QUIRK_K8_BSTEP_NOTEMP, + QUIRK_K8_REVFG_TEMP, + QUIRK_AMD_ERRATA_319 } quirk_id_t; typedef struct { diff --git a/system/msr.h b/system/msr.h index 167cf72..f4b98d8 100644 --- a/system/msr.h +++ b/system/msr.h @@ -30,6 +30,9 @@ #define MSR_AMD64_NB_CFG 0xc001001f #define MSR_AMD64_COFVID_STATUS 0xc0010071 +#define MSR_VIA_TEMP_C7 0x1169 +#define MSR_VIA_TEMP_NANO 0x1423 + #define rdmsr(msr, value1, value2) \ __asm__ __volatile__("rdmsr" \ : "=a" (value1), \ diff --git a/system/temperature.c b/system/temperature.c index 4d3c0aa..1bcc945 100644 --- a/system/temperature.c +++ b/system/temperature.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. +// Copyright (C) 2004-2023 Sam Demeulemeester. // // Derived from an extract of memtest86+ init.c: // @@ -16,18 +17,32 @@ #include "cpuid.h" #include "cpuinfo.h" +#include "hwquirks.h" #include "msr.h" #include "pci.h" #include "temperature.h" +//------------------------------------------------------------------------------ +// Public Variables +//------------------------------------------------------------------------------ + +float cpu_temp_offset = 0; + //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ +void temperature_init(void) +{ + // Process temperature-related quirks + if (quirk.type & QUIRK_TYPE_TEMP) { + quirk.process(); + } +} + int get_cpu_temperature(void) { - // Intel CPU if (cpuid_info.vendor_id.str[0] == 'G' && cpuid_info.max_cpuid >= 6) { if (cpuid_info.dts_pmp & 1) { @@ -47,26 +62,32 @@ int get_cpu_temperature(void) } // AMD CPU - else if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.extendedFamily > 0 && cpuid_info.version.extendedFamily < 8) { + else if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF) { // Target only K8 & newer - // Untested yet - uint32_t rtcr = pci_config_read32(0, 24, 3, 0xA4); - int raw_temp = (rtcr >> 21) & 0x7FF; + if (cpuid_info.version.extendedFamily >= 8) { // Target Zen µarch and newer. Use SMN to get temperature. - return raw_temp / 8; + uint32_t tval = amd_smn_read(SMN_THM_TCON_CUR_TMP); - } else if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.extendedFamily >= 8) { + if ((tval >> 19) & 0x01) { + cpu_temp_offset = -49.0f; + } - // Grab CPU Temp. for ZEN CPUs using SNM - uint32_t tval = amd_smn_read(SMN_THM_TCON_CUR_TMP); + return cpu_temp_offset + 0.125f * (float)((tval >> 21) & 0x7FF); - float offset = 0; + } else if (cpuid_info.version.extendedFamily > 0) { // Target K10 to K15 (Bulldozer) - if((tval >> 19) & 0x01) { - offset = -49.0f; + uint32_t rtcr = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K10); + int raw_temp = ((rtcr >> 21) & 0x7FF) / 8; + + return (raw_temp > 0) ? raw_temp : 0; + + } else { // Target K8 (CPUID ExtFamily = 0) + + uint32_t rtcr = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K8); + int raw_temp = ((rtcr >> 16) & 0xFF) - 49 + cpu_temp_offset; + + return (raw_temp > 0) ? raw_temp : 0; } - - return offset + 0.125f * (float)((tval >> 21) & 0x7FF); } // VIA/Centaur/Zhaoxin CPU @@ -76,9 +97,9 @@ int get_cpu_temperature(void) uint32_t msrl, msrh, msr_temp; if (cpuid_info.version.family == 7 || cpuid_info.version.model == 0xF) { - msr_temp = 0x1423; // Zhaoxin, Nano + msr_temp = MSR_VIA_TEMP_NANO; // Zhaoxin, Nano } else if (cpuid_info.version.model == 0xA || cpuid_info.version.model == 0xD) { - msr_temp = 0x1169; // C7 A/D + msr_temp = MSR_VIA_TEMP_C7; // C7 A/D } else { return 0; } diff --git a/system/temperature.h b/system/temperature.h index 73fdcef..f39c418 100644 --- a/system/temperature.h +++ b/system/temperature.h @@ -8,8 +8,22 @@ * *//* * Copyright (C) 2020-2022 Martin Whitaker. + * Copyright (C) 2003-2023 Sam Demeulemeester. */ +#define AMD_TEMP_REG_K8 0xE4 +#define AMD_TEMP_REG_K10 0xA4 + +/** + * Global CPU Temperature offset + */ +extern float cpu_temp_offset; + +/** + * Init temperature sensor and compute offsets if needed + */ +void temperature_init(void); + /** * Returns the current temperature of the CPU. Returns 0 if * the temperature cannot be read.