coreboot/util/amdtool/cpu.c
Michał Żygowski 7436c59875 util/amdtool: Add support for Phoenix AM5 CPUs
Add register tables and device IDs for Phoenix AM5 desktop CPUs.

TEST=Dump all data with amdtool on MSI PRO B650M-A.

Change-Id: Ia7af9194fb7516e98b7cddee2bfc65af12d56dc0
Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/90009
Reviewed-by: Michał Kopeć <michal.kopec@3mdeb.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2025-11-18 13:30:29 +00:00

565 lines
13 KiB
C

/* amdtool - dump all registers on an AMD CPU + chipset based system */
/* SPDX-License-Identifier: GPL-2.0-only */
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include "amdtool.h"
#include "smn.h"
#ifdef __x86_64__
# define BREG "%%rbx"
#else
# define BREG "%%ebx"
#endif
typedef struct {
uint32_t number;
char *name;
} msr_entry_t;
int fd_msr;
uint32_t cpuid(uint32_t eax)
{
uint32_t ret;
#if defined(__PIC__) || defined(__DARWIN__) && !defined(__LP64__)
asm volatile (
"push " BREG "\n\t"
"cpuid\n\t"
"pop " BREG "\n\t"
: "=a" (ret) : "a" (eax) : "%ecx", "%edx"
);
#else
asm ("cpuid" : "=a" (ret) : "a" (eax) : "%ebx", "%ecx", "%edx");
#endif
return ret;
}
static inline cpuid_result_t cpuid_ext(uint32_t eax, unsigned int ecx)
{
cpuid_result_t result;
#ifndef __DARWIN__
asm volatile (
"mov %%ebx, %%edi;"
"cpuid;"
"mov %%ebx, %%esi;"
"mov %%edi, %%ebx;"
: "=a" (result.eax),
"=S" (result.ebx),
"=c" (result.ecx),
"=d" (result.edx)
: "0" (eax), "2" (ecx)
: "edi");
#endif
return result;
}
#ifndef __DARWIN__
int msr_readerror = 0;
static msr_t rdmsr(unsigned int addr)
{
uint32_t buf[2];
msr_t msr = { 0xffffffff, 0xffffffff };
if (lseek(fd_msr, (off_t) addr, SEEK_SET) == -1) {
perror("Could not lseek() to MSR");
close(fd_msr);
exit(1);
}
if (read(fd_msr, buf, 8) == 8) {
msr.lo = buf[0];
msr.hi = buf[1];
return msr;
}
if (errno == 5) {
printf(" (*)"); // Not all bits of the MSR could be read
msr_readerror = 1;
} else {
// A severe error.
perror("Could not read() MSR");
close(fd_msr);
exit(1);
}
return msr;
}
static int open_and_seek(int cpu, unsigned long msr, int mode, int *fd)
{
char dev[32];
char temp_string[50];
snprintf(dev, sizeof(dev), "/dev/cpu/%d/msr", cpu);
*fd = open(dev, mode);
if (*fd < 0) {
snprintf(temp_string, sizeof(temp_string), "open(\"%s\")", dev);
perror(temp_string);
return -1;
}
if (lseek(*fd, msr, SEEK_SET) == (off_t)-1) {
snprintf(temp_string, sizeof(temp_string), "lseek(%lu)", msr);
perror(temp_string);
close(*fd);
return -1;
}
return 0;
}
static msr_t rdmsr_from_cpu(int cpu, unsigned long addr)
{
int fd;
msr_t msr = { 0xffffffff, 0xffffffff };
uint32_t buf[2];
char temp_string[50];
if (open_and_seek(cpu, addr, O_RDONLY, &fd) < 0) {
snprintf(temp_string, sizeof(temp_string),
"Could not read MSR for CPU#%d", cpu);
perror(temp_string);
}
if (read(fd, buf, 8) == 8) {
msr.lo = buf[0];
msr.hi = buf[1];
}
close(fd);
return msr;
}
static int get_number_of_cpus(void)
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
static bool is_sme_supported(void)
{
cpuid_result_t cpuid_regs;
if (cpuid(0x80000000) < 0x8000001f)
return false;
cpuid_regs = cpuid_ext(0x8000001f, 0x0);
return !!(cpuid_regs.eax & 1);
}
static bool is_sme_enabled(int cpunum)
{
msr_t data;
data = rdmsr_from_cpu(cpunum, 0xC0010010);
return !!(data.lo & (1 << 23));
}
#endif
static int print_sme(void)
{
int error = -1;
#ifndef __DARWIN__
int ncpus = get_number_of_cpus();
int i = 0;
bool sme_supported;
printf("\n============= Dumping AMD SME status =============\n");
if (ncpus < 1) {
perror("Failed to get number of CPUs");
error = -1;
} else {
sme_supported = is_sme_supported();
for (i = 0; i < ncpus ; i++) {
printf("------------- CPU %d ----------------\n", i);
printf("SME supported : %s\n",
sme_supported ? "YES" : "NO");
if (sme_supported)
printf("SME enabled : %s\n",
is_sme_enabled(i) ? "YES" : "NO");
}
error = 0;
}
printf("====================================================\n\n");
#endif
return error;
}
static bool is_sev_supported(void)
{
cpuid_result_t cpuid_regs;
if (cpuid(0x80000000) < 0x8000001f)
return false;
cpuid_regs = cpuid_ext(0x8000001f, 0x0);
return !!(cpuid_regs.eax & 2);
}
static bool is_sev_es_supported(void)
{
cpuid_result_t cpuid_regs;
if (cpuid(0x80000000) < 0x8000001f)
return false;
cpuid_regs = cpuid_ext(0x8000001f, 0x0);
return !!(cpuid_regs.eax & 8);
}
static bool is_sev_snp_supported(void)
{
cpuid_result_t cpuid_regs;
if (cpuid(0x80000000) < 0x8000001f)
return false;
cpuid_regs = cpuid_ext(0x8000001f, 0x0);
return !!(cpuid_regs.eax & 0x10);
}
static bool is_sev_enabled(int cpunum)
{
msr_t data;
data = rdmsr_from_cpu(cpunum, 0xC0010131);
return !!(data.lo & 1);
}
static bool is_sev_es_enabled(int cpunum)
{
msr_t data;
data = rdmsr_from_cpu(cpunum, 0xC0010131);
return !!(data.lo & 2);
}
static bool is_sev_snp_enabled(int cpunum)
{
msr_t data;
data = rdmsr_from_cpu(cpunum, 0xC0010131);
return !!(data.lo & 4);
}
static unsigned int get_sev_max_guest_num(void)
{
cpuid_result_t cpuid_regs;
cpuid_regs = cpuid_ext(0x8000001f, 0x0);
return cpuid_regs.ecx;
}
static unsigned int get_sev_min_asid(void)
{
cpuid_result_t cpuid_regs;
cpuid_regs = cpuid_ext(0x8000001f, 0x0);
return cpuid_regs.edx;
}
static int print_sev(void)
{
int error = -1;
#ifndef __DARWIN__
int ncpus = get_number_of_cpus();
int i = 0;
bool sev_supported, sev_es_supported, sev_snp_supported;
unsigned int max_guest, min_asid;
printf("\n============= Dumping AMD SEV status =============\n");
if (ncpus < 1) {
perror("Failed to get number of CPUs");
error = -1;
} else {
/*
* The following use CPUID, so should be the same for each core
* in the scope of processor.
*/
sev_supported = is_sev_supported();
sev_es_supported = is_sev_es_supported();
sev_snp_supported = is_sev_snp_supported();
if (sev_supported) {
max_guest = get_sev_max_guest_num();
min_asid = get_sev_min_asid();
}
for (i = 0; i < ncpus ; i++) {
printf("------------- CPU %d ----------------\n", i);
printf("SEV supported : %s\n",
sev_supported ? "YES" : "NO");
if (sev_supported) {
printf("Max SEV encrypted guests : %u\n",
max_guest);
printf("Min SEV ASID : %u\n",
min_asid);
printf("SEV enabled : %s\n",
is_sev_enabled(i) ? "YES" : "NO");
}
printf("SEV-ES supported : %s\n",
sev_es_supported ? "YES" : "NO");
if (sev_es_supported)
printf("SEV-ES enabled : %s\n",
is_sev_es_enabled(i) ? "YES" : "NO");
printf("SEV-SNP supported : %s\n",
sev_snp_supported ? "YES" : "NO");
if (sev_snp_supported)
printf("SEV-SNP enabled : %s\n",
is_sev_snp_enabled(i) ? "YES" : "NO");
}
error = 0;
}
printf("====================================================");
#endif
return error;
}
static void get_cpu_brand_string(char *cpu_string)
{
u32 tmp[13];
cpuid_result_t res;
const char *str = "Unknown Processor Name";
int i, j;
if (cpuid(0x80000000) >= 0x80000004) {
j = 0;
for (i = 0; i < 3; i++) {
res = cpuid_ext(0x80000002 + i, 0x0);
tmp[j++] = res.eax;
tmp[j++] = res.ebx;
tmp[j++] = res.ecx;
tmp[j++] = res.edx;
}
tmp[12] = 0;
str = (const char *)tmp;
}
strcpy(cpu_string, str);
}
#define CPU_BRAND_STRING_LEN 48
static int print_cpu_features(void)
{
int error = -1;
#ifndef __DARWIN__
cpuid_result_t cpuid_regs;
printf("\n============= AMD CPU features =============\n");
if (cpuid(0x80000000) >= 0x80000001) {
cpuid_regs = cpuid_ext(0x80000001, 0x0);
printf("SVM supported : %s\n",
cpuid_regs.ecx & (1 << 2) ? "YES" : "NO");
printf("SKINIT supported : %s\n",
cpuid_regs.ecx & (1 << 12) ? "YES" : "NO");
}
error = 0;
printf("====================================================\n");
#endif
return error;
}
int print_cpu_info(void)
{
int ret;
char brand_string[CPU_BRAND_STRING_LEN + 1];
get_cpu_brand_string(brand_string);
printf("CPU brand string: %s\n", brand_string);
ret = print_cpu_features();
ret += print_sme();
ret += print_sev();
return ret;
}
static const msr_entry_t common_msrs[] = {
{ 0x001b, "IA32_APIC_BASE" },
{ 0x008b, "MICROCODE_PATCH_LEVEL" },
{ 0x00fe, "IA32_MTRRCAP" },
{ 0x0179, "IA32_MCG_CAP" },
{ 0x017a, "IA32_MCG_STATUS" },
{ 0x017b, "IA32_MCG_CONTROL" },
{ 0x01d9, "IA32_DEBUGCTL" },
{ 0x0200, "IA32_MTRR_PHYSBASE0" },
{ 0x0201, "IA32_MTRR_PHYSMASK0" },
{ 0x0202, "IA32_MTRR_PHYSBASE1" },
{ 0x0203, "IA32_MTRR_PHYSMASK1" },
{ 0x0204, "IA32_MTRR_PHYSBASE2" },
{ 0x0205, "IA32_MTRR_PHYSMASK2" },
{ 0x0206, "IA32_MTRR_PHYSBASE3" },
{ 0x0207, "IA32_MTRR_PHYSMASK3" },
{ 0x0208, "IA32_MTRR_PHYSBASE4" },
{ 0x0209, "IA32_MTRR_PHYSMASK4" },
{ 0x020a, "IA32_MTRR_PHYSBASE5" },
{ 0x020b, "IA32_MTRR_PHYSMASK5" },
{ 0x020c, "IA32_MTRR_PHYSBASE6" },
{ 0x020d, "IA32_MTRR_PHYSMASK6" },
{ 0x020e, "IA32_MTRR_PHYSBASE7" },
{ 0x020f, "IA32_MTRR_PHYSMASK7" },
{ 0x0250, "IA32_MTRR_FIX64K_00000" },
{ 0x0258, "IA32_MTRR_FIX16K_80000" },
{ 0x0259, "IA32_MTRR_FIX16K_A0000" },
{ 0x0268, "IA32_MTRR_FIX4K_C0000" },
{ 0x0269, "IA32_MTRR_FIX4K_C8000" },
{ 0x026a, "IA32_MTRR_FIX4K_D0000" },
{ 0x026b, "IA32_MTRR_FIX4K_D8000" },
{ 0x026c, "IA32_MTRR_FIX4K_E0000" },
{ 0x026d, "IA32_MTRR_FIX4K_E8000" },
{ 0x026e, "IA32_MTRR_FIX4K_F0000" },
{ 0x026f, "IA32_MTRR_FIX4K_F8000" },
{ 0x0277, "IA32_PAT" },
{ 0x02ff, "IA32_MTRR_DEF_TYPE" },
{ 0xc0000080, "EFER" },
{ 0xc0010010, "SYS_CFG" },
{ 0xc0010015, "HWCR" },
{ 0xc0010016, "IORR_BASE0" },
{ 0xc0010017, "IORR_MASK0" },
{ 0xc0010018, "IORR_BASE1" },
{ 0xc0010019, "IORR_MASK1" },
{ 0xc001001a, "TOP_MEM" },
{ 0xc001001d, "TOM2" },
{ 0xc0010030, "PROCESSOR_NAME_STRING0" },
{ 0xc0010031, "PROCESSOR_NAME_STRING1" },
{ 0xc0010032, "PROCESSOR_NAME_STRING2" },
{ 0xc0010033, "PROCESSOR_NAME_STRING3" },
{ 0xc0010034, "PROCESSOR_NAME_STRING4" },
{ 0xc0010035, "PROCESSOR_NAME_STRING5" },
{ 0xc0010050, "SMI_ON_IO_TRAP0" },
{ 0xc0010051, "SMI_ON_IO_TRAP1" },
{ 0xc0010052, "SMI_ON_IO_TRAP2" },
{ 0xc0010053, "SMI_ON_IO_TRAP3" },
{ 0xc0010054, "SMI_ON_IO_TRAP_CTL_STS" },
{ 0xc0010056, "SMI_TRIGGER_IO_CYCLE" },
{ 0xc0010058, "MMCONF_BASE_ADDR" },
{ 0xc0010061, "PSTATE_CURRENT_LIMIT" },
{ 0xc0010062, "PSTATE_CNTRL" },
{ 0xc0010063, "PSTATE_STATUS" },
{ 0xc0010064, "PSTATE0_DEF" },
{ 0xc0010065, "PSTATE1_DEF" },
{ 0xc0010066, "PSTATE2_DEF" },
{ 0xc0010067, "PSTATE3_DEF" },
{ 0xc0010068, "PSTATE4_DEF" },
{ 0xc0010069, "PSTATE5_DEF" },
{ 0xc001006a, "PSTATE6_DEF" },
{ 0xc001006b, "PSTATE7_DEF" },
{ 0xc0010073, "CSTATE_BASE_ADDR" },
{ 0xc0010074, "CPU_WDT_CFG" },
{ 0xc0010111, "SMM_BASE" },
{ 0xc0010112, "SMM_TSEG_BASE" },
{ 0xc0010113, "SMM_TSEG_MASK" },
{ 0xc0010114, "VM_CR" },
{ 0xc0010118, "SVM_LOCK_KEY" },
{ 0xc0010119, "SMM_LOCK_KEY" },
{ 0xc0010131, "SEV_STATUS" },
{ 0xc0010140, "OSVW_ID_LENGTH" },
{ 0xc0010141, "OSVW_STATUS" },
{ 0xc0010292, "PWR_MNGMNT_MISC" },
{ 0xc0010293, "HW_PSTATE_STATUS" },
{ 0xc0010294, "CSTATE_POLICY" },
{ 0xc0010296, "CSTATE_CONFIG" },
{ 0xc0010297, "PWR_MNGMNT_DEFAULT" },
{ 0xc0010299, "RAPL_PWR_UNIT" },
{ 0xc001029a, "CORE_ENERGY_STS" },
{ 0xc001029b, "PKG_ENERGY_STS" },
{ 0xc00102b0, "CPPC_CAP1" },
{ 0xc00102b1, "CPPC_ENABLE" },
{ 0xc00102b2, "CPPC_CAP2" },
{ 0xc00102b3, "CPPC_REQUEST" },
{ 0xc00102b4, "CPPC_STATUS" },
{ 0xc0011002, "CPUID_7_FEATURES" },
{ 0xc0011003, "CPUID_PWR_THERM" },
{ 0xc0011004, "CPUID_FEATURES" },
{ 0xc0011005, "CPUID_EXT_FEATURES" },
{ 0xc001100c, "NODE_ID/SCRATCH" },
{ 0xC0011020, "LS_CFG" },
{ 0xC0011021, "IC_CFG" },
{ 0xc0011022, "DC_CFG" },
{ 0xc0011023, "TW_CFG" },
{ 0xc0011028, "FP_CFG" },
{ 0xc0011029, "ME_CFG" },
{ 0xc001102a, "BU_CFG2" },
{ 0xc001102b, "L2_PFCFG" },
{ 0xC001102d, "LS_CFG2" },
{ 0xc001102e, "BP_CFG" },
{ 0xc00110a2, "PSP_ADDR" },
};
int print_amd_msrs(void)
{
unsigned int i, id;
msr_t msr;
typedef struct {
unsigned int model;
const msr_entry_t *global_msrs;
unsigned int num_global_msrs;
} cpu_t;
cpu_t cpulist[] = {
{ CPUID_TURIN_C1, common_msrs, ARRAY_SIZE(common_msrs) },
{ CPUID_PHOENIX_A2, common_msrs, ARRAY_SIZE(common_msrs) },
};
cpu_t *cpu = NULL;
/* Get CPU family, model and stepping */
id = cpuid(1);
for (i = 0; i < ARRAY_SIZE(cpulist); i++) {
if(cpulist[i].model == id) {
cpu = &cpulist[i];
break;
}
}
if (!cpu) {
printf("Error: Dumping MSRs on this CPU (0x%06x) is not (yet) supported.\n", id);
return -1;
}
#ifndef __DARWIN__
fd_msr = open("/dev/cpu/0/msr", O_RDWR);
if (fd_msr < 0) {
perror("Error while opening /dev/cpu/0/msr");
printf("Did you run 'modprobe msr'?\n");
return -1;
}
#endif
printf("\n===================== MSRs =====================\n");
for (i = 0; i < cpu->num_global_msrs; i++) {
msr = rdmsr(cpu->global_msrs[i].number);
printf(" MSR 0x%08X = 0x%08X:0x%08X (%s)\n",
cpu->global_msrs[i].number, msr.hi, msr.lo,
cpu->global_msrs[i].name);
}
#ifndef __DARWIN__
close(fd_msr);
if (msr_readerror)
printf("\n(*) Some MSRs could not be read. The marked values are unreliable.\n");
#endif
return 0;
}