[PATCH 0/6] x86: Correct timer overflow and add msr/cpuid commands

This series corrects an overflow of the timer which happens on QEMU on newer hardware. It also adds commands to read/write CPU ID registers and machine-status registers (MSRs).
Simon Glass (6): x86: Show the CPU vendor in bdinfo x86: Ensure the CPU identity exists for timer init x86: Avoid timer-clock overflow x86: Sync up tsc_timer with Linux x86: Add a cpuid command x86: Add msr command
arch/x86/cpu/i386/cpu.c | 99 +++++++++++++++++++------------ arch/x86/cpu/x86_64/cpu.c | 6 ++ arch/x86/include/asm/u-boot-x86.h | 17 ++++++ arch/x86/lib/bdinfo.c | 7 ++- cmd/x86/Makefile | 2 +- cmd/x86/cpuid.c | 37 ++++++++++++ cmd/x86/msr.c | 52 ++++++++++++++++ doc/usage/cmd/cpuid.rst | 68 +++++++++++++++++++++ doc/usage/cmd/msr.rst | 61 +++++++++++++++++++ doc/usage/index.rst | 2 + drivers/timer/tsc_timer.c | 18 +++++- test/cmd/Makefile | 1 + test/cmd/cpuid.c | 22 +++++++ test/cmd/msr.c | 38 ++++++++++++ 14 files changed, 387 insertions(+), 43 deletions(-) create mode 100644 cmd/x86/cpuid.c create mode 100644 cmd/x86/msr.c create mode 100644 doc/usage/cmd/cpuid.rst create mode 100644 doc/usage/cmd/msr.rst create mode 100644 test/cmd/cpuid.c create mode 100644 test/cmd/msr.c

Refactor the cpu code and use it to show the CPU vendor, e.g. AuthenticAMD or GenuineIntel
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/x86/cpu/i386/cpu.c | 94 ++++++++++++++++++------------- arch/x86/include/asm/u-boot-x86.h | 8 +++ arch/x86/lib/bdinfo.c | 7 ++- 3 files changed, 69 insertions(+), 40 deletions(-)
diff --git a/arch/x86/cpu/i386/cpu.c b/arch/x86/cpu/i386/cpu.c index 934e98ac582..101837b3190 100644 --- a/arch/x86/cpu/i386/cpu.c +++ b/arch/x86/cpu/i386/cpu.c @@ -263,6 +263,49 @@ static int build_vendor_name(char *vendor_name) } #endif
+int x86_cpu_vendor_info(char *name) +{ + uint cpu_device; + + cpu_device = 0; + + /* gcc 7.3 does not want to drop x86_vendors, so use #ifdef */ +#ifndef CONFIG_TPL_BUILD + *name = '\0'; /* Unset */ + + /* Find the id and vendor_name */ + if (!has_cpuid()) { + /* Its a 486 if we can modify the AC flag */ + if (flag_is_changeable_p(X86_EFLAGS_AC)) + cpu_device = 0x00000400; /* 486 */ + else + cpu_device = 0x00000300; /* 386 */ + if (cpu_device == 0x00000400 && test_cyrix_52div()) { + /* If we ever care we can enable cpuid here */ + memcpy(name, "CyrixInstead", 13); + + /* Detect NexGen with old hypercode */ + } else if (deep_magic_nexgen_probe()) { + memcpy(name, "NexGenDriven", 13); + } + } else { + int cpuid_level; + + cpuid_level = build_vendor_name(name); + name[12] = '\0'; + + /* Intel-defined flags: level 0x00000001 */ + if (cpuid_level >= 0x00000001) + cpu_device = cpuid_eax(0x00000001); + else + /* Have CPUID level 0 only unheard of */ + cpu_device = 0x00000400; + } +#endif /* CONFIG_TPL_BUILD */ + + return cpu_device; +} + static void identify_cpu(struct cpu_device_id *cpu) { cpu->device = 0; /* fix gcc 4.4.4 warning */ @@ -289,46 +332,19 @@ static void identify_cpu(struct cpu_device_id *cpu) return; }
-/* gcc 7.3 does not want to drop x86_vendors, so use #ifdef */ #ifndef CONFIG_TPL_BUILD - char vendor_name[16]; - int i; - - vendor_name[0] = '\0'; /* Unset */ - - /* Find the id and vendor_name */ - if (!has_cpuid()) { - /* Its a 486 if we can modify the AC flag */ - if (flag_is_changeable_p(X86_EFLAGS_AC)) - cpu->device = 0x00000400; /* 486 */ - else - cpu->device = 0x00000300; /* 386 */ - if ((cpu->device == 0x00000400) && test_cyrix_52div()) { - memcpy(vendor_name, "CyrixInstead", 13); - /* If we ever care we can enable cpuid here */ - } - /* Detect NexGen with old hypercode */ - else if (deep_magic_nexgen_probe()) - memcpy(vendor_name, "NexGenDriven", 13); - } else { - int cpuid_level; - - cpuid_level = build_vendor_name(vendor_name); - vendor_name[12] = '\0'; - - /* Intel-defined flags: level 0x00000001 */ - if (cpuid_level >= 0x00000001) { - cpu->device = cpuid_eax(0x00000001); - } else { - /* Have CPUID level 0 only unheard of */ - cpu->device = 0x00000400; - } - } - cpu->vendor = X86_VENDOR_UNKNOWN; - for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) { - if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) { - cpu->vendor = x86_vendors[i].vendor; - break; + { + char vendor_name[16]; + int i; + + cpu->device = x86_cpu_vendor_info(vendor_name); + + cpu->vendor = X86_VENDOR_UNKNOWN; + for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) { + if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) { + cpu->vendor = x86_vendors[i].vendor; + break; + } } } #endif diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h index 3acc58ad74b..5cc8f63334e 100644 --- a/arch/x86/include/asm/u-boot-x86.h +++ b/arch/x86/include/asm/u-boot-x86.h @@ -51,6 +51,14 @@ int x86_cpu_init_tpl(void); */ void cpu_reinit_fpu(void);
+/** + * x86_cpu_vendor_info() - Get the CPU-vendor name and device number + * + * @name: 13-byte area to hold the returned string + * Return: CPU device number read from cpuid + */ +int x86_cpu_vendor_info(char *name); + int cpu_init_f(void); void setup_gdt(struct global_data *id, u64 *gdt_addr); /* diff --git a/arch/x86/lib/bdinfo.c b/arch/x86/lib/bdinfo.c index 165e8ab944f..2a78f578dee 100644 --- a/arch/x86/lib/bdinfo.c +++ b/arch/x86/lib/bdinfo.c @@ -19,7 +19,12 @@ void arch_print_bdinfo(void) bdinfo_print_num_l("clock_rate", gd->arch.clock_rate); bdinfo_print_num_l("tsc_base", gd->arch.tsc_base); bdinfo_print_num_l("vendor", gd->arch.x86_vendor); - bdinfo_print_str(" name", cpu_vendor_name(gd->arch.x86_vendor)); + if (!IS_ENABLED(CONFIG_X86_64)) { + char vendor_name[16]; + + x86_cpu_vendor_info(vendor_name); + bdinfo_print_str(" name", vendor_name); + } bdinfo_print_num_l("model", gd->arch.x86_model); bdinfo_print_num_l("phys_addr in bits", cpu_phys_address_size()); bdinfo_print_num_l("table start", gd->arch.table_start);

Refactor the cpu code and use it to show the CPU vendor, e.g. AuthenticAMD or GenuineIntel
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/x86/cpu/i386/cpu.c | 94 ++++++++++++++++++------------- arch/x86/include/asm/u-boot-x86.h | 8 +++ arch/x86/lib/bdinfo.c | 7 ++- 3 files changed, 69 insertions(+), 40 deletions(-)
Applied to u-boot-dm, thanks!

When bootstage is used the timer can be inited before the CPU identity is set up, resulting in the checks for the vendor not working.
Add a special call to work around this.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/x86/cpu/i386/cpu.c | 5 +++++ arch/x86/cpu/x86_64/cpu.c | 6 ++++++ arch/x86/include/asm/u-boot-x86.h | 9 +++++++++ drivers/timer/tsc_timer.c | 4 ++++ 4 files changed, 24 insertions(+)
diff --git a/arch/x86/cpu/i386/cpu.c b/arch/x86/cpu/i386/cpu.c index 101837b3190..3726f7ff78f 100644 --- a/arch/x86/cpu/i386/cpu.c +++ b/arch/x86/cpu/i386/cpu.c @@ -501,6 +501,11 @@ int x86_cpu_reinit_f(void) return 0; }
+void x86_get_identity_for_timer(void) +{ + setup_identity(); +} + void x86_enable_caches(void) { unsigned long cr0; diff --git a/arch/x86/cpu/x86_64/cpu.c b/arch/x86/cpu/x86_64/cpu.c index 80eab710315..71bc07f872a 100644 --- a/arch/x86/cpu/x86_64/cpu.c +++ b/arch/x86/cpu/x86_64/cpu.c @@ -75,3 +75,9 @@ void board_debug_uart_init(void) /* this was already done in SPL */ } #endif + +void x86_get_identity_for_timer(void) +{ + /* set the vendor to Intel so that native_calibrate_tsc() works */ + gd->arch.x86_vendor = X86_VENDOR_INTEL; +} diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h index 5cc8f63334e..64139ead190 100644 --- a/arch/x86/include/asm/u-boot-x86.h +++ b/arch/x86/include/asm/u-boot-x86.h @@ -43,6 +43,15 @@ int x86_cpu_reinit_f(void); */ int x86_cpu_init_tpl(void);
+/** + * x86_get_identity_for_timer() - Set up CPU identity for use by the early timer + * + * The timer can be needed early in board_f if bootstage is enabled. This + * function can be called from the TSC timer to make sure that the CPU-identity + * info has been set up + */ +void x86_get_identity_for_timer(void); + /** * cpu_reinit_fpu() - Reinit the FPU if something is wrong with it * diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c index 80c084f380d..d11227cf440 100644 --- a/drivers/timer/tsc_timer.c +++ b/drivers/timer/tsc_timer.c @@ -403,6 +403,10 @@ static void tsc_timer_ensure_setup(bool early) if (!gd->arch.clock_rate) { unsigned long fast_calibrate;
+ /* deal with this being called before x86_cpu_init_f() */ + if (!gd->arch.x86_vendor) + x86_get_identity_for_timer(); + /** * There is no obvious way to obtain this information from EFI * boot services. This value was measured on a Framework Laptop

When bootstage is used the timer can be inited before the CPU identity is set up, resulting in the checks for the vendor not working.
Add a special call to work around this.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/x86/cpu/i386/cpu.c | 5 +++++ arch/x86/cpu/x86_64/cpu.c | 6 ++++++ arch/x86/include/asm/u-boot-x86.h | 9 +++++++++ drivers/timer/tsc_timer.c | 4 ++++ 4 files changed, 24 insertions(+)
Applied to u-boot-dm, thanks!

When the clock speed is above about 4GHz, e.g. on modern PC hardware, the timer overflows, resulting in a much lower frequency than expected. Deal with this by capping the clock speed.
It would be possible to move to a 64-bit value for the clock, but that is a pain to deal with. A better approach might be to express the clock in MHz but that is left for later consideration.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/timer/tsc_timer.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c index d11227cf440..2f2c2f27b7f 100644 --- a/drivers/timer/tsc_timer.c +++ b/drivers/timer/tsc_timer.c @@ -442,6 +442,7 @@ static void tsc_timer_ensure_setup(bool early) return;
done: + fast_calibrate = min(fast_calibrate, 4000UL); if (!gd->arch.clock_rate) gd->arch.clock_rate = fast_calibrate * 1000000; }

When the clock speed is above about 4GHz, e.g. on modern PC hardware, the timer overflows, resulting in a much lower frequency than expected. Deal with this by capping the clock speed.
It would be possible to move to a 64-bit value for the clock, but that is a pain to deal with. A better approach might be to express the clock in MHz but that is left for later consideration.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/timer/tsc_timer.c | 1 + 1 file changed, 1 insertion(+)
Applied to u-boot-dm, thanks!

Since we are using the code from Linux, update it to the newer version in v6.11
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/timer/tsc_timer.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c index 2f2c2f27b7f..dd16ab320b2 100644 --- a/drivers/timer/tsc_timer.c +++ b/drivers/timer/tsc_timer.c @@ -83,7 +83,7 @@ static unsigned long cpu_mhz_from_cpuid(void) if (cpuid_eax(0) < 0x16) return 0;
- return cpuid_eax(0x16); + return cpuid_eax(0x15); }
/* @@ -299,10 +299,19 @@ static unsigned long __maybe_unused quick_pit_calibrate(void) if (!pit_expect_msb(0xff-i, &delta, &d2)) break;
+ delta -= tsc; + + /* + * Extrapolate the error and fail fast if the error will + * never be below 500 ppm. + */ + if (i == 1 && + d1 + d2 >= (delta * MAX_QUICK_PIT_ITERATIONS) >> 11) + return 0; + /* * Iterate until the error is less than 500 ppm */ - delta -= tsc; if (d1+d2 >= delta >> 11) continue;

Since we are using the code from Linux, update it to the newer version in v6.11
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/timer/tsc_timer.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
Applied to u-boot-dm, thanks!

It is useful to obtain the results of cpuid queries, so add a command for this.
Signed-off-by: Simon Glass sjg@chromium.org ---
cmd/x86/Makefile | 2 +- cmd/x86/cpuid.c | 37 ++++++++++++++++++++++ doc/usage/cmd/cpuid.rst | 68 +++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + test/cmd/Makefile | 1 + test/cmd/cpuid.c | 22 +++++++++++++ 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 cmd/x86/cpuid.c create mode 100644 doc/usage/cmd/cpuid.rst create mode 100644 test/cmd/cpuid.c
diff --git a/cmd/x86/Makefile b/cmd/x86/Makefile index b1f39d3bfde..1648907ac2d 100644 --- a/cmd/x86/Makefile +++ b/cmd/x86/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o -obj-y += mtrr.o +obj-y += cpuid.o mtrr.o obj-$(CONFIG_CMD_EXCEPTION) += exception.o obj-$(CONFIG_USE_HOB) += hob.o obj-$(CONFIG_HAVE_FSP) += fsp.o diff --git a/cmd/x86/cpuid.c b/cmd/x86/cpuid.c new file mode 100644 index 00000000000..222754b5d15 --- /dev/null +++ b/cmd/x86/cpuid.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'cpuid' command provides access to the CPU's cpuid information + * + * Copyright 2024 Google, LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <command.h> +#include <vsprintf.h> +#include <asm/cpu.h> + +static int do_cpuid(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cpuid_result res; + ulong op; + + if (argc < 2) + return CMD_RET_USAGE; + + op = hextoul(argv[1], NULL); + res = cpuid(op); + printf("eax %08x\n", res.eax); + printf("ebx %08x\n", res.ebx); + printf("ecx %08x\n", res.ecx); + printf("edx %08x\n", res.edx); + + return 0; +} + +U_BOOT_LONGHELP(cpuid, "Show CPU Identification information"); + +U_BOOT_CMD( + cpuid, 2, 1, do_cpuid, + "cpuid <op>", cpuid_help_text +); diff --git a/doc/usage/cmd/cpuid.rst b/doc/usage/cmd/cpuid.rst new file mode 100644 index 00000000000..cccf9262ed4 --- /dev/null +++ b/doc/usage/cmd/cpuid.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. index:: + single: cpuid (command) + +cpuid command +============= + +Synopsis +-------- + +:: + + cpuid <op> + +Description +----------- + +The cpuid command requests CPU-identification information on x86 CPUs. The +operation <op> selects what information is returned. Up to four 32-bit registers +can be update (eax-edx) depending on the operation. + +Configuration +------------- + +The cpuid command is only available on x86. + +Return value +------------ + +The return value $? is 0 (true). + +Example +------- + +:: + + => cpuid 1 + eax 00060fb1 + ebx 00040800 + ecx 80002001 + edx 178bfbfd + +This shows checking for 64-bit 'long' mode:: + + => cpuid 80000000 + eax 8000000a + ebx 68747541 + ecx 444d4163 + edx 69746e65 + => cpuid 80000001 + eax 00060fb1 + ebx 00000000 + ecx 00000007 + edx 2193fbfd # Bit 29 is set in edx, so long mode is available + +On a 32-bit-only CPU:: + + => cpuid 80000000 + eax 80000004 + ebx 756e6547 + ecx 6c65746e + edx 49656e69 + => cpuid 80000001 + eax 00000663 + ebx 00000000 + ecx 00000000 + edx 00000000 # Bit 29 is not set in edx, so long mode is not available diff --git a/doc/usage/index.rst b/doc/usage/index.rst index b058c2254fb..986554090af 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -52,6 +52,7 @@ Shell commands cmd/conitrace cmd/cp cmd/cpu + cmd/cpuid cmd/cyclic cmd/dm cmd/ebtupdate diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 8f2134998ad..dedcec33c8e 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -12,6 +12,7 @@ ifdef CONFIG_CONSOLE_RECORD obj-$(CONFIG_CMD_PAUSE) += test_pause.o endif obj-y += exit.o mem.o +obj-$(CONFIG_X86) += cpuid.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_FDT) += fdt.o diff --git a/test/cmd/cpuid.c b/test/cmd/cpuid.c new file mode 100644 index 00000000000..e07f5fd4696 --- /dev/null +++ b/test/cmd/cpuid.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for cpuid command + * + * Copyright 2024 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <test/cmd.h> +#include <test/ut.h> + +static int cmd_test_cpuid(struct unit_test_state *uts) +{ + ut_assertok(run_commandf("cpuid 1")); + ut_assert_nextline("eax 00060fb1"); + ut_assert_nextline("ebx 00000800"); + ut_assert_nextline("ecx 80002001"); + ut_assert_nextline("edx 078bfbfd"); + + return 0; +} +CMD_TEST(cmd_test_cpuid, UTF_CONSOLE);

It is useful to obtain the results of cpuid queries, so add a command for this.
Signed-off-by: Simon Glass sjg@chromium.org ---
cmd/x86/Makefile | 2 +- cmd/x86/cpuid.c | 37 ++++++++++++++++++++++ doc/usage/cmd/cpuid.rst | 68 +++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + test/cmd/Makefile | 1 + test/cmd/cpuid.c | 22 +++++++++++++ 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 cmd/x86/cpuid.c create mode 100644 doc/usage/cmd/cpuid.rst create mode 100644 test/cmd/cpuid.c
Applied to u-boot-dm, thanks!

It is useful to obtain the results of MSR queries as well as to update MSR registers, so add a command these tasks.
Signed-off-by: Simon Glass sjg@chromium.org ---
cmd/x86/Makefile | 2 +- cmd/x86/msr.c | 52 ++++++++++++++++++++++++++++++++++++ doc/usage/cmd/msr.rst | 61 +++++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + test/cmd/Makefile | 2 +- test/cmd/msr.c | 38 +++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 cmd/x86/msr.c create mode 100644 doc/usage/cmd/msr.rst create mode 100644 test/cmd/msr.c
diff --git a/cmd/x86/Makefile b/cmd/x86/Makefile index 1648907ac2d..925215235d3 100644 --- a/cmd/x86/Makefile +++ b/cmd/x86/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o -obj-y += cpuid.o mtrr.o +obj-y += cpuid.o msr.o mtrr.o obj-$(CONFIG_CMD_EXCEPTION) += exception.o obj-$(CONFIG_USE_HOB) += hob.o obj-$(CONFIG_HAVE_FSP) += fsp.o diff --git a/cmd/x86/msr.c b/cmd/x86/msr.c new file mode 100644 index 00000000000..3cb70d1f89a --- /dev/null +++ b/cmd/x86/msr.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'cpuid' command provides access to the CPU's cpuid information + * + * Copyright 2024 Google, LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <command.h> +#include <vsprintf.h> +#include <asm/msr.h> + +static int do_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct msr_t msr; + ulong op; + + if (argc < 2) + return CMD_RET_USAGE; + + op = hextoul(argv[1], NULL); + msr = msr_read(op); + printf("%08x %08x\n", msr.hi, msr.lo); + + return 0; +} + +static int do_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct msr_t msr; + ulong op; + + if (argc < 4) + return CMD_RET_USAGE; + + op = hextoul(argv[1], NULL); + msr.hi = hextoul(argv[2], NULL); + msr.lo = hextoul(argv[3], NULL); + msr_write(op, msr); + + return 0; +} + +U_BOOT_LONGHELP(msr, + "read <op> - read a machine-status register (MSR) as <hi 32-bits> <lo 32-bits>\n" + "write <op< <hi> <lo> - write an MSR"); + +U_BOOT_CMD_WITH_SUBCMDS(msr, "Machine Status Registers", msr_help_text, + U_BOOT_CMD_MKENT(read, CONFIG_SYS_MAXARGS, 1, do_read, "", ""), + U_BOOT_CMD_MKENT(write, CONFIG_SYS_MAXARGS, 1, do_write, "", "")); diff --git a/doc/usage/cmd/msr.rst b/doc/usage/cmd/msr.rst new file mode 100644 index 00000000000..04ee52cc1c7 --- /dev/null +++ b/doc/usage/cmd/msr.rst @@ -0,0 +1,61 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. index:: + single: msr (command) + +msr command +=========== + +Synopsis +-------- + +:: + + msr read <op> + msr write <op> <hi> <lo> + +Description +----------- + +The msr command reads and writes machine-status registers (MSRs) on x86 CPUs. +The information is a 64-bit value split into two parts, <hi> for the top 32 +bits and <lo> for the bottom 32 bits. + +The operation <op> selects what information is read or written. + +msr read +~~~~~~~~ + +This reads an MSR and displays the value obtained. + +msr write +~~~~~~~~~ + +This writes a value to an MSR. + +Configuration +------------- + +The msr command is only available on x86. + +Return value +------------ + +The return value $? is 0 (true). + +Example +------- + +This shows reading msr 0x194 which is MSR_FLEX_RATIO on Intel CPUs:: + + => msr read 194 + 00000000 00011200 # Bits 16 (flex ratio enable) and 20 (lock) are set + +This shows adjusting the energy-performance bias on an Intel CPU:: + + => msr read 1b0 + 00000000 00000006 # 6 means 'normal' + + => msr write 1b0 0 f # change to power-save + => msr read 1b0 + 00000000 0000000f diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 986554090af..2e60afb297e 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -87,6 +87,7 @@ Shell commands cmd/mbr cmd/md cmd/mmc + cmd/msr cmd/mtest cmd/mtrr cmd/panic diff --git a/test/cmd/Makefile b/test/cmd/Makefile index dedcec33c8e..fb6795a87a5 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -12,7 +12,7 @@ ifdef CONFIG_CONSOLE_RECORD obj-$(CONFIG_CMD_PAUSE) += test_pause.o endif obj-y += exit.o mem.o -obj-$(CONFIG_X86) += cpuid.o +obj-$(CONFIG_X86) += cpuid.o msr.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_FDT) += fdt.o diff --git a/test/cmd/msr.c b/test/cmd/msr.c new file mode 100644 index 00000000000..e9a152ee5bf --- /dev/null +++ b/test/cmd/msr.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for msr command + * + * Copyright 2024 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <test/cmd.h> +#include <test/ut.h> + +static int cmd_test_msr(struct unit_test_state *uts) +{ + ut_assertok(run_commandf("msr read 200")); + ut_assert_nextline("00000000 ffe00006"); + ut_assert_console_end(); + + /* change the first variable msr and see it reflected in the mtrr cmd */ + ut_assertok(run_commandf("mtrr")); + ut_assert_nextline("CPU 65537:"); + ut_assert_nextlinen("Reg"); + ut_assert_nextlinen("0 Y Back 00000000ffe00000"); + ut_assertok(console_record_reset_enable()); + + /* change the type from 6 to 5 */ + ut_assertok(run_commandf("msr write 200 0 ffe00005")); + ut_assert_console_end(); + + /* Now it shows 'Protect' */ + ut_assertok(run_commandf("mtrr")); + ut_assert_nextline("CPU 65537:"); + ut_assert_nextlinen("Reg"); + ut_assert_nextlinen("0 Y Protect 00000000ffe00000"); + ut_assertok(console_record_reset_enable()); + + return 0; +} +CMD_TEST(cmd_test_msr, UTF_CONSOLE);

It is useful to obtain the results of MSR queries as well as to update MSR registers, so add a command these tasks.
Signed-off-by: Simon Glass sjg@chromium.org ---
cmd/x86/Makefile | 2 +- cmd/x86/msr.c | 52 ++++++++++++++++++++++++++++++++++++ doc/usage/cmd/msr.rst | 61 +++++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + test/cmd/Makefile | 2 +- test/cmd/msr.c | 38 +++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 cmd/x86/msr.c create mode 100644 doc/usage/cmd/msr.rst create mode 100644 test/cmd/msr.c
Applied to u-boot-dm, thanks!
participants (1)
-
Simon Glass