aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 79efedaac1b75ddadc2286241b5dc715e0ed0df6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# CPU Internals Driver (cpui-drv)

A driver that allows users to interact with various internal components of the CPU. Implemented and tested with Linux kernel v6.2

## CPU IDentification (CPUID)

The software needs a way to find out if particular features are available (for example, 64-bit mode, Hardware Virtualization, HyperThreading, etc). The CPUID instruction is a mechanism that can be used to retrieve such information to understand whether or not CPU supports these features.

CPUID does NOT have operands. Rather it takes input as a value preloaded into _eax_ (it's _eax_ even on 64-bit systems, not _rax_) and possibly _ecx_. After the instruction is executed, the outputs are stored to _eax_, _ebx_, _ecx_, and _edx_.

### How to Check for CPUID Support

CPUID didn't exist on the first systems so we actually have to check if the hardware supports CPUID (added in late i486 models):

```
The ID flag (bit 21) in the EFLAGS register indicates support for the CPUID instruction. If a software procedure can set and clear this flag, the processor executing the procedure supports the CPUID instruction.
```

To manipulate with the EFLAGS register values, we need `PUSHF` / `POPF` instructions, they push / pop lower 16 bits of EFLAGS to the stack.

That's how it's done in the Linux kernel ([arch/x86/kernel/cpu/common.c](https://github.com/torvalds/linux/blob/c9c3395d5e3dcc6daee66c6908354d47bf98cb0c/arch/x86/kernel/cpu/common.c#L304)):

``` c
/* Standard macro to see if a specific flag is changeable */
static inline int flag_is_changeable_p(u32 flag)
{
  u32 f1, f2;
  asm volatile (
    "pushfl      \n\t" // Push the current value of the EFLAGS
    "pushfl      \n\t" // register onto the stack twice.
    "popl %0     \n\t" // Pop the original EFLAGS value into the var `f1`
    "movl %0, %1 \n\t" // Move the value of `f1` into `f2`
    "xorl %2, %0 \n\t" // XOR op. between `flag` and `f1`
    "pushl %0    \n\t" // Push the modified value of `f1` onto the stack and..
    "popfl       \n\t" // Pop it into the EFLAGS register
    "pushfl      \n\t" // Push the current value of the EFLAGS
                       // register onto the stack
    "popl %0     \n\t" // Pop it into `f2`
    "popfl       \n\t" // Restore the original value of the EFLAGS

    : "=&r" (f1), "=&r" (f2)
    : "ir" (flag));

  return ((f1^f2) & flag) != 0;
}

/* Probe for the CPUID instruction */
int have_cpuid_p(void)
{
  return flag_is_changeable_p(X86_EFLAGS_ID);
}
```

`X86_EFLAGS_ID` is defined in [arch/x86/include/uapi/asm/processor-flags.h](https://github.com/torvalds/linux/blob/c9c3395d5e3dcc6daee66c6908354d47bf98cb0c/arch/x86/include/uapi/asm/processor-flags.h#L46) :

``` c
#define X86_EFLAGS_ID_BIT	21 /* CPUID detection */
#define X86_EFLAGS_ID		_BITUL(X86_EFLAGS_ID_BIT)
```

To obtain the table with possible inputs and outputs, use _Intel 64 and IA-32 Architectures Software Developer's Manual,  Volume 2, Chapter 3.3 Instructions, CPUID - CPU Identification_.

## Model Specific Registers (MSRs)

After the fact, when you find out that some features you want to use are supported, you want to have a mechanism for configuring them. Over time, this list of MSRs has grown so large that it has become a separate volume of the Intel manuals - _Volume 4: Model-Specific Registers_.

Many MSRs have carried over from one generation of IA-32 processors to the next and to Intel 64 processors. A subset of MSRs and associated bit fields, which do **NOT** change on future processor generations, are now considered **architectural MSRs**. For historical reasons (beginning with the Pentium 4 processor), these “**architectural MSRs**” were given the prefix “**IA32_**” (it doesn't mean it's restricted to 32-bit execution).

`RDMSR` / `WRMSR` are privileged instructions, so they cannot be used in user-space, only in kernel-mode.

Take a look how the Linux kernel uses these instructions in C code ([arch/x86/include/asm/msr.h](https://github.com/torvalds/linux/blob/c9c3395d5e3dcc6daee66c6908354d47bf98cb0c/arch/x86/include/asm/msr.h)):

``` c
/*
 * both i386 and x86_64 returns 64-bit value in edx:eax, but gcc's "A"
 * constraint has different meanings. For i386, "A" means exactly
 * edx:eax, while for x86_64 it doesn't mean rdx:rax or edx:eax. Instead,
 * it means rax *or* rdx.
 */
#ifdef CONFIG_X86_64
  /* Using 64-bit values saves one instruction clearing the high half of low */
  #define DECLARE_ARGS(val, low, high)  unsigned long low, high
  #define EAX_EDX_VAL(val, low, high) ((low) | (high) << 32)
  #define EAX_EDX_RET(val, low, high) "=a" (low), "=d" (high)
#else
  #define DECLARE_ARGS(val, low, high)  unsigned long long val
  #define EAX_EDX_VAL(val, low, high) (val)
  #define EAX_EDX_RET(val, low, high) "=A" (val)
#endif

...

static __always_inline unsigned long long __rdmsr(unsigned int msr)
{
  DECLARE_ARGS(val, low, high);

  asm volatile("1: rdmsr\n"
         "2:\n"
         _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_RDMSR)
         : EAX_EDX_RET(val, low, high) : "c" (msr));

  return EAX_EDX_VAL(val, low, high);
}

static __always_inline void __wrmsr(unsigned int msr, u32 low, u32 high)
{
  asm volatile("1: wrmsr\n"
         "2:\n"
         _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_WRMSR)
         : : "c" (msr), "a"(low), "d" (high) : "memory");
}
```

## Resources

* [Intel 64 and IA-32 Architectures Software Developer's Manual, Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4](https://cdrdv2.intel.com/v1/dl/getContent/671200)
* [AMD CPUID Specification](https://www.amd.com/system/files/TechDocs/25481.pdf)