From 591541af667dbf3617b81d89f92abf01e9e1a16d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Jun 2012 16:08:24 +0100 Subject: [PATCH] [cmdline] Add "cpuid" command Allow x86 CPU feature flags (such as support for 64-bit mode) to be checked using the "cpuid" command. Signed-off-by: Michael Brown --- src/arch/i386/core/cpu.c | 73 ------------ src/arch/i386/include/bits/cpu.h | 86 -------------- src/arch/x86/Makefile | 1 + src/arch/x86/core/cpuid.c | 154 ++++++++++++++++++++++++++ src/arch/x86/hci/commands/cpuid_cmd.c | 94 ++++++++++++++++ src/arch/x86/include/bits/errfile.h | 6 +- src/arch/x86/include/ipxe/cpuid.h | 53 +++++++++ src/config/config.c | 3 + src/config/defaults/pcbios.h | 1 + 9 files changed, 310 insertions(+), 161 deletions(-) delete mode 100644 src/arch/i386/core/cpu.c delete mode 100644 src/arch/i386/include/bits/cpu.h create mode 100644 src/arch/x86/core/cpuid.c create mode 100644 src/arch/x86/hci/commands/cpuid_cmd.c create mode 100644 src/arch/x86/include/ipxe/cpuid.h diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c deleted file mode 100644 index c24fa4e6..00000000 --- a/src/arch/i386/core/cpu.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include - -/** @file - * - * CPU identification - * - */ - -/** - * Test to see if CPU flag is changeable - * - * @v flag Flag to test - * @ret can_change Flag is changeable - */ -static inline int flag_is_changeable ( unsigned int flag ) { - uint32_t f1, f2; - - __asm__ ( "pushfl\n\t" - "pushfl\n\t" - "popl %0\n\t" - "movl %0,%1\n\t" - "xorl %2,%0\n\t" - "pushl %0\n\t" - "popfl\n\t" - "pushfl\n\t" - "popl %0\n\t" - "popfl\n\t" - : "=&r" ( f1 ), "=&r" ( f2 ) - : "ir" ( flag ) ); - - return ( ( ( f1 ^ f2 ) & flag ) != 0 ); -} - -/** - * Get CPU information - * - * @v cpu CPU information structure to fill in - */ -void get_cpuinfo ( struct cpuinfo_x86 *cpu ) { - unsigned int cpuid_level; - unsigned int cpuid_extlevel; - unsigned int discard_1, discard_2, discard_3; - - memset ( cpu, 0, sizeof ( *cpu ) ); - - /* Check for CPUID instruction */ - if ( ! flag_is_changeable ( X86_EFLAGS_ID ) ) { - DBG ( "CPUID not supported\n" ); - return; - } - - /* Get features, if present */ - cpuid ( 0x00000000, &cpuid_level, &discard_1, - &discard_2, &discard_3 ); - if ( cpuid_level >= 0x00000001 ) { - cpuid ( 0x00000001, &discard_1, &discard_2, - &discard_3, &cpu->features ); - } else { - DBG ( "CPUID cannot return capabilities\n" ); - } - - /* Get 64-bit features, if present */ - cpuid ( 0x80000000, &cpuid_extlevel, &discard_1, - &discard_2, &discard_3 ); - if ( ( cpuid_extlevel & 0xffff0000 ) == 0x80000000 ) { - if ( cpuid_extlevel >= 0x80000001 ) { - cpuid ( 0x80000001, &discard_1, &discard_2, - &discard_3, &cpu->amd_features ); - } - } -} diff --git a/src/arch/i386/include/bits/cpu.h b/src/arch/i386/include/bits/cpu.h deleted file mode 100644 index 83339ddd..00000000 --- a/src/arch/i386/include/bits/cpu.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef I386_BITS_CPU_H -#define I386_BITS_CPU_H - -/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ -#define X86_FEATURE_FPU 0 /* Onboard FPU */ -#define X86_FEATURE_VME 1 /* Virtual Mode Extensions */ -#define X86_FEATURE_DE 2 /* Debugging Extensions */ -#define X86_FEATURE_PSE 3 /* Page Size Extensions */ -#define X86_FEATURE_TSC 4 /* Time Stamp Counter */ -#define X86_FEATURE_MSR 5 /* Model-Specific Registers, RDMSR, WRMSR */ -#define X86_FEATURE_PAE 6 /* Physical Address Extensions */ -#define X86_FEATURE_MCE 7 /* Machine Check Architecture */ -#define X86_FEATURE_CX8 8 /* CMPXCHG8 instruction */ -#define X86_FEATURE_APIC 9 /* Onboard APIC */ -#define X86_FEATURE_SEP 11 /* SYSENTER/SYSEXIT */ -#define X86_FEATURE_MTRR 12 /* Memory Type Range Registers */ -#define X86_FEATURE_PGE 13 /* Page Global Enable */ -#define X86_FEATURE_MCA 14 /* Machine Check Architecture */ -#define X86_FEATURE_CMOV 15 /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ -#define X86_FEATURE_PAT 16 /* Page Attribute Table */ -#define X86_FEATURE_PSE36 17 /* 36-bit PSEs */ -#define X86_FEATURE_PN 18 /* Processor serial number */ -#define X86_FEATURE_CLFLSH 19 /* Supports the CLFLUSH instruction */ -#define X86_FEATURE_DTES 21 /* Debug Trace Store */ -#define X86_FEATURE_ACPI 22 /* ACPI via MSR */ -#define X86_FEATURE_MMX 23 /* Multimedia Extensions */ -#define X86_FEATURE_FXSR 24 /* FXSAVE and FXRSTOR instructions (fast save and restore */ - /* of FPU context), and CR4.OSFXSR available */ -#define X86_FEATURE_XMM 25 /* Streaming SIMD Extensions */ -#define X86_FEATURE_XMM2 26 /* Streaming SIMD Extensions-2 */ -#define X86_FEATURE_SELFSNOOP 27 /* CPU self snoop */ -#define X86_FEATURE_HT 28 /* Hyper-Threading */ -#define X86_FEATURE_ACC 29 /* Automatic clock control */ -#define X86_FEATURE_IA64 30 /* IA-64 processor */ - -/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */ -/* Don't duplicate feature flags which are redundant with Intel! */ -#define X86_FEATURE_SYSCALL 11 /* SYSCALL/SYSRET */ -#define X86_FEATURE_MMXEXT 22 /* AMD MMX extensions */ -#define X86_FEATURE_LM 29 /* Long Mode (x86-64) */ -#define X86_FEATURE_3DNOWEXT 30 /* AMD 3DNow! extensions */ -#define X86_FEATURE_3DNOW 31 /* 3DNow! */ - -/** x86 CPU information */ -struct cpuinfo_x86 { - /** CPU features */ - unsigned int features; - /** 64-bit CPU features */ - unsigned int amd_features; -}; - -/* - * EFLAGS bits - */ -#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */ -#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */ -#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */ -#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */ -#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */ -#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */ -#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */ -#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */ -#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */ -#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */ -#define X86_EFLAGS_NT 0x00004000 /* Nested Task */ -#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */ -#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */ -#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */ -#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */ -#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */ -#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */ - -/* - * Generic CPUID function - */ -static inline __attribute__ (( always_inline )) void -cpuid ( int op, unsigned int *eax, unsigned int *ebx, - unsigned int *ecx, unsigned int *edx ) { - __asm__ ( "cpuid" : - "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) - : "0" ( op ) ); -} - -extern void get_cpuinfo ( struct cpuinfo_x86 *cpu ); - -#endif /* I386_BITS_CPU_H */ diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 37e03aaf..cdd397d4 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -7,6 +7,7 @@ INCDIRS += arch/x86/include SRCDIRS += arch/x86/core SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/prefix +SRCDIRS += arch/x86/hci/commands # breaks building some of the linux-related objects CFLAGS += -Ulinux diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c new file mode 100644 index 00000000..cf9dd6dd --- /dev/null +++ b/src/arch/x86/core/cpuid.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** @file + * + * x86 CPU feature detection + * + */ + +/** + * Check whether or not CPUID instruction is supported + * + * @ret is_supported CPUID instruction is supported + */ +static int cpuid_is_supported ( void ) { + unsigned long original; + unsigned long inverted; + + __asm__ ( "pushf\n\t" + "pushf\n\t" + "pop %0\n\t" + "mov %0,%1\n\t" + "xor %2,%1\n\t" + "push %1\n\t" + "popf\n\t" + "pushf\n\t" + "pop %1\n\t" + "popf\n\t" + : "=&r" ( original ), "=&r" ( inverted ) + : "ir" ( CPUID_FLAG ) ); + return ( ( original ^ inverted ) & CPUID_FLAG ); +} + +/** + * Issue CPUID instruction + * + * @v operation CPUID operation + * @v eax Output via %eax + * @v ebx Output via %ebx + * @v ecx Output via %ecx + * @v edx Output via %edx + */ +static inline __attribute__ (( always_inline )) void +cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, + uint32_t *edx ) { + + __asm__ ( "cpuid" + : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) + : "0" ( operation ) ); +} + +/** + * Get Intel-defined x86 CPU features + * + * @v features x86 CPU features to fill in + */ +static void x86_intel_features ( struct x86_features *features ) { + uint32_t max_level; + uint32_t discard_a; + uint32_t discard_b; + uint32_t discard_c; + uint32_t discard_d; + + /* Check that features are available via CPUID */ + cpuid ( CPUID_VENDOR_ID, &max_level, &discard_b, &discard_c, + &discard_d ); + if ( max_level < CPUID_FEATURES ) { + DBGC ( features, "CPUID has no Intel-defined features (max " + "level %08x)\n", max_level ); + return; + } + + /* Get features */ + cpuid ( CPUID_FEATURES, &discard_a, &discard_b, + &features->intel.ecx, &features->intel.edx ); + DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n", + features->intel.ecx, features->intel.edx ); + +} + +/** + * Get AMD-defined x86 CPU features + * + * @v features x86 CPU features to fill in + */ +static void x86_amd_features ( struct x86_features *features ) { + uint32_t max_level; + uint32_t discard_a; + uint32_t discard_b; + uint32_t discard_c; + uint32_t discard_d; + + /* Check that features are available via CPUID */ + cpuid ( CPUID_AMD_MAX_FN, &max_level, &discard_b, &discard_c, + &discard_d ); + if ( ( max_level & CPUID_AMD_CHECK_MASK ) != CPUID_AMD_CHECK ) { + DBGC ( features, "CPUID has no extended functions\n" ); + return; + } + if ( max_level < CPUID_AMD_FEATURES ) { + DBGC ( features, "CPUID has no AMD-defined features (max " + "level %08x)\n", max_level ); + return; + } + + /* Get features */ + cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b, + &features->amd.ecx, &features->amd.edx ); + DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n", + features->amd.ecx, features->amd.edx ); +} + +/** + * Get x86 CPU features + * + * @v features x86 CPU features to fill in + */ +void x86_features ( struct x86_features *features ) { + + /* Clear all features */ + memset ( features, 0, sizeof ( *features ) ); + + /* Check that CPUID instruction is available */ + if ( ! cpuid_is_supported() ) { + DBGC ( features, "CPUID instruction is not supported\n" ); + return; + } + + /* Get Intel-defined features */ + x86_intel_features ( features ); + + /* Get AMD-defined features */ + x86_amd_features ( features ); +} diff --git a/src/arch/x86/hci/commands/cpuid_cmd.c b/src/arch/x86/hci/commands/cpuid_cmd.c new file mode 100644 index 00000000..3172727e --- /dev/null +++ b/src/arch/x86/hci/commands/cpuid_cmd.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * x86 CPU feature detection command + * + */ + +/** "cpuid" options */ +struct cpuid_options { + /** Check AMD-defined features (%eax=0x80000001) */ + int amd; + /** Check features defined via %ecx */ + int ecx; +}; + +/** "cpuid" option list */ +static struct option_descriptor cpuid_opts[] = { + OPTION_DESC ( "amd", 'a', no_argument, + struct cpuid_options, amd, parse_flag ), + OPTION_DESC ( "ecx", 'c', no_argument, + struct cpuid_options, ecx, parse_flag ), +}; + +/** "cpuid" command descriptor */ +static struct command_descriptor cpuid_cmd = + COMMAND_DESC ( struct cpuid_options, cpuid_opts, 1, 1, + "[--amd] [--ecx] " ); + +/** + * The "cpuid" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int cpuid_exec ( int argc, char **argv ) { + struct cpuid_options opts; + struct x86_features features; + struct x86_feature_registers *feature_regs; + uint32_t feature_reg; + unsigned int bit; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &cpuid_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse bit number */ + if ( ( rc = parse_integer ( argv[optind], &bit ) ) != 0 ) + return rc; + + /* Get CPU features */ + x86_features ( &features ); + + /* Extract relevant feature register */ + feature_regs = ( opts.amd ? &features.amd : &features.intel ); + feature_reg = ( opts.ecx ? feature_regs->ecx : feature_regs->edx ); + + /* Check presence of specified feature */ + return ( ( feature_reg & ( 1 << bit ) ) ? 0 : -ENOENT ); +} + +/** x86 CPU feature detection command */ +struct command cpuid_command __command = { + .name = "cpuid", + .exec = cpuid_exec, +}; diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index ccf00b86..f66cbaa1 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -38,8 +38,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_undionly ( ERRFILE_ARCH | ERRFILE_NET | 0x00030000 ) #define ERRFILE_undirom ( ERRFILE_ARCH | ERRFILE_NET | 0x00040000 ) -#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) -#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) +#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) +#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) + +#define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) /** @} */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h new file mode 100644 index 00000000..9705137c --- /dev/null +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -0,0 +1,53 @@ +#ifndef _IPXE_CPUID_H +#define _IPXE_CPUID_H + +/** @file + * + * x86 CPU feature detection + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +/** An x86 CPU feature register set */ +struct x86_feature_registers { + /** Features returned via %ecx */ + uint32_t ecx; + /** Features returned via %edx */ + uint32_t edx; +}; + +/** x86 CPU features */ +struct x86_features { + /** Intel-defined features (%eax=0x00000001) */ + struct x86_feature_registers intel; + /** AMD-defined features (%eax=0x80000001) */ + struct x86_feature_registers amd; +}; + +/** CPUID support flag */ +#define CPUID_FLAG 0x00200000UL + +/** Get vendor ID and largest standard function */ +#define CPUID_VENDOR_ID 0x00000000UL + +/** Get standard features */ +#define CPUID_FEATURES 0x00000001UL + +/** Get largest extended function */ +#define CPUID_AMD_MAX_FN 0x80000000UL + +/** Extended function existence check */ +#define CPUID_AMD_CHECK 0x80000000UL + +/** Extended function existence check mask */ +#define CPUID_AMD_CHECK_MASK 0xffff0000UL + +/** Get extended features */ +#define CPUID_AMD_FEATURES 0x80000001UL + +extern void x86_features ( struct x86_features *features ); + +#endif /* _IPXE_CPUID_H */ diff --git a/src/config/config.c b/src/config/config.c index 9c1df1f3..eebd849b 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -238,6 +238,9 @@ REQUIRE_OBJECT ( vlan_cmd ); #ifdef REBOOT_CMD REQUIRE_OBJECT ( reboot_cmd ); #endif +#ifdef CPUID_CMD +REQUIRE_OBJECT ( cpuid_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index b68186f1..c52fca97 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -36,5 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SANBOOT_PROTO_FCP /* Fibre Channel protocol */ #define REBOOT_CMD /* Reboot command */ +#define CPUID_CMD /* x86 CPU feature detection command */ #endif /* CONFIG_DEFAULTS_PCBIOS_H */