1 | /*␊ |
2 | * Copyright 2008 Islam Ahmed Zaid. All rights reserved. <azismed@gmail.com>␊ |
3 | * AsereBLN: 2009: cleanup and bugfix␊ |
4 | */␊ |
5 | ␊ |
6 | #ifndef __LIBSAIO_CPU_H␊ |
7 | #define __LIBSAIO_CPU_H␊ |
8 | ␊ |
9 | //#include "libsaio.h"␊ |
10 | ␊ |
11 | extern void scan_cpu(PlatformInfo_t *);␊ |
12 | ␊ |
13 | #define bit(n)␉␉␉(1ULL << (n))␊ |
14 | #define bitmask(h,l)␉␉((bit(h)|(bit(h)-1)) & ~(bit(l)-1))␊ |
15 | #define bitfield(x,h,l)␉␉(((x) & bitmask(h,l)) >> l)␊ |
16 | ␊ |
17 | #define CPU_STRING_UNKNOWN␉␉"Unknown CPU Type"␊ |
18 | ␊ |
19 | #define MSR_IA32_PLATFORM_ID 0x17␊ |
20 | #define␉MSR_IA32_PERF_STATUS 0x198␊ |
21 | #define MSR_IA32_PERF_CONTROL 0x199␊ |
22 | #define MSR_IA32_EXT_CONFIG 0xEE␊ |
23 | #define MSR_FLEX_RATIO 0x194␊ |
24 | #define MSR_TURBO_RATIO_LIMIT 0x1AD␊ |
25 | #define␉MSR_PLATFORM_INFO 0xCE␊ |
26 | #define MSR_CORE_THREAD_COUNT 0x35␉␉␉// Undocumented␊ |
27 | ␊ |
28 | #define K8_FIDVID_STATUS 0xC0010042␊ |
29 | #define K10_COFVID_STATUS 0xC0010071␊ |
30 | ␊ |
31 | #define MSR_AMD_MPERF 0x000000E7␊ |
32 | #define MSR_AMD_APERF 0x000000E8␊ |
33 | ␊ |
34 | #define DEFAULT_FSB␉␉100000 /* for now, hardcoding 100MHz for old CPUs */␊ |
35 | ␊ |
36 | // DFE: This constant comes from older xnu:␊ |
37 | #define CLKNUM␉␉␉1193182␉␉/* formerly 1193167 */␊ |
38 | ␊ |
39 | // DFE: These two constants come from Linux except CLOCK_TICK_RATE replaced with CLKNUM␊ |
40 | #define CALIBRATE_TIME_MSEC␉30␉␉/* 30 msecs */␊ |
41 | #define CALIBRATE_LATCH␉␉((CLKNUM * CALIBRATE_TIME_MSEC + 1000/2)/1000)␊ |
42 | ␊ |
43 | // CPUID Values␊ |
44 | ␊ |
45 | #define CPUID_MODEL_PRESCOTT␉␉3␉// Celeron D, Pentium 4 (90nm)␊ |
46 | #define CPUID_MODEL_NOCONA␉␉4␉// Xeon Nocona, Irwindale (90nm)␊ |
47 | #define CPUID_MODEL_PRESLER␉␉6␉// Pentium 4, Pentium D (65nm)␊ |
48 | #define CPUID_MODEL_DOTHAN␉␉13␉// Dothan␊ |
49 | #define CPUID_MODEL_YONAH␉␉14␉// Intel Mobile Core Solo, Duo␊ |
50 | #define CPUID_MODEL_MEROM␉␉15␉// Intel Mobile Core 2 Solo, Duo, Xeon 30xx, Xeon 51xx, Xeon X53xx, Xeon E53xx, Xeon X32xx␊ |
51 | #define CPUID_MODEL_CONROE␉␉15␊ |
52 | #define CPUID_MODEL_CELERON␉␉22␊ |
53 | #define CPUID_MODEL_PENRYN␉␉23␉// Intel Core 2 Solo, Duo, Quad, Extreme, Xeon X54xx, Xeon X33xx␊ |
54 | #define CPUID_MODEL_WOLFDALE␉␉23␊ |
55 | #define CPUID_MODEL_NEHALEM␉␉26␉// Intel Core i7, Xeon W35xx, Xeon X55xx, Xeon E55xx LGA1366 (45nm)␊ |
56 | #define CPUID_MODEL_ATOM␉␉28␉// Intel Atom (45nm) Pineview, Silverthorne␊ |
57 | #define CPUID_MODEL_XEON_MP␉␉29␊ |
58 | #define CPUID_MODEL_FIELDS␉␉30␉// Intel Core i5, i7, Xeon X34xx LGA1156 (45nm),(Clarksfiled, Lynnfield, Jasper Forest)␊ |
59 | #define CPUID_MODEL_DALES␉␉31␉// Havendale, Auburndale␊ |
60 | #define CPUID_MODEL_CLARKDALE␉␉37␉// Intel Core i3, i5 LGA1156 (32nm), (Arrandale, Clarksdale)␊ |
61 | #define CPUID_MODEL_LINCROFT␉␉38␉// Intel Atom (45nm) Z6xx (single core)␊ |
62 | #define CPUID_MODEL_SANDYBRIDGE␉␉42␉// Intel Core i3, i5, i7 LGA1155 (32nm)␊ |
63 | #define CPUID_MODEL_WESTMERE␉␉44␉// Intel Core i7, Xeon X56xx, Xeon E56xx, Xeon W36xx LGA1366 (32nm) 6 Core␊ |
64 | #define CPUID_MODEL_JAKETOWN␉␉45␉// Intel Xeon E5 LGA2011 (32nm), SandyBridge-E, SandyBridge-EN, SandyBridge-EP␊ |
65 | #define CPUID_MODEL_NEHALEM_EX␉␉46␉// Intel Xeon X75xx, Xeon X65xx, Xeon E75xx, Xeon E65x␊ |
66 | #define CPUID_MODEL_WESTMERE_EX␉␉47␉// Intel Xeon E7␊ |
67 | #define CPUID_MODEL_CEDARVIEW␉␉54␉// Intel Atom (32nm)␊ |
68 | #define CPUID_MODEL_IVYBRIDGE␉␉58␉// Intel Core i5, i7 LGA1155 (22nm)␊ |
69 | ␊ |
70 | static inline uint64_t rdtsc64(void)␊ |
71 | {␊ |
72 | ␉uint64_t ret;␊ |
73 | ␉__asm__ volatile("rdtsc" : "=A" (ret));␊ |
74 | ␉return ret;␊ |
75 | }␊ |
76 | ␊ |
77 | static inline uint64_t rdmsr64(uint32_t msr)␊ |
78 | {␊ |
79 | uint64_t ret;␊ |
80 | __asm__ volatile("rdmsr" : "=A" (ret) : "c" (msr));␊ |
81 | return ret;␊ |
82 | }␊ |
83 | ␊ |
84 | static inline void wrmsr64(uint32_t msr, uint64_t val)␊ |
85 | {␊ |
86 | ␉__asm__ volatile("wrmsr" : : "c" (msr), "A" (val));␊ |
87 | }␊ |
88 | ␊ |
89 | static inline void intel_waitforsts(void) {␊ |
90 | ␉uint32_t inline_timeout = 100000;␊ |
91 | ␉while (rdmsr64(MSR_IA32_PERF_STATUS) & (1 << 21)) { if (!inline_timeout--) break; }␊ |
92 | }␊ |
93 | ␊ |
94 | static inline void do_cpuid(uint32_t selector, uint32_t *data)␊ |
95 | {␊ |
96 | ␉asm volatile ("cpuid"␊ |
97 | ␉␉␉␉ : "=a" (data[0]),␊ |
98 | ␉␉␉␉ "=b" (data[1]),␊ |
99 | ␉␉␉␉ "=c" (data[2]),␊ |
100 | ␉␉␉␉ "=d" (data[3])␊ |
101 | ␉␉␉␉ : "a" (selector));␊ |
102 | }␊ |
103 | ␊ |
104 | static inline void do_cpuid2(uint32_t selector, uint32_t selector2, uint32_t *data)␊ |
105 | {␊ |
106 | ␉asm volatile ("cpuid"␊ |
107 | ␉␉␉␉ : "=a" (data[0]),␊ |
108 | ␉␉␉␉ "=b" (data[1]),␊ |
109 | ␉␉␉␉ "=c" (data[2]),␊ |
110 | ␉␉␉␉ "=d" (data[3])␊ |
111 | ␉␉␉␉ : "a" (selector), "c" (selector2));␊ |
112 | }␊ |
113 | ␊ |
114 | // DFE: enable_PIT2 and disable_PIT2 come from older xnu␊ |
115 | ␊ |
116 | /*␊ |
117 | * Enable or disable timer 2.␊ |
118 | * Port 0x61 controls timer 2:␊ |
119 | * bit 0 gates the clock,␊ |
120 | * bit 1 gates output to speaker.␊ |
121 | */␊ |
122 | static inline void enable_PIT2(void)␊ |
123 | {␊ |
124 | /* Enable gate, disable speaker */␊ |
125 | __asm__ volatile(␊ |
126 | ␉␉␉␉␉ " inb $0x61,%%al \n\t"␊ |
127 | ␉␉␉␉␉ " and $0xFC,%%al \n\t" /* & ~0x03 */␊ |
128 | ␉␉␉␉␉ " or $1,%%al \n\t"␊ |
129 | ␉␉␉␉␉ " outb %%al,$0x61 \n\t"␊ |
130 | ␉␉␉␉␉ : : : "%al" );␊ |
131 | }␊ |
132 | ␊ |
133 | static inline void disable_PIT2(void)␊ |
134 | {␊ |
135 | /* Disable gate and output to speaker */␊ |
136 | __asm__ volatile(␊ |
137 | ␉␉␉␉␉ " inb $0x61,%%al \n\t"␊ |
138 | ␉␉␉␉␉ " and $0xFC,%%al \n\t"␉/* & ~0x03 */␊ |
139 | ␉␉␉␉␉ " outb %%al,$0x61 \n\t"␊ |
140 | ␉␉␉␉␉ : : : "%al" );␊ |
141 | }␊ |
142 | ␊ |
143 | // DFE: set_PIT2_mode0, poll_PIT2_gate, and measure_tsc_frequency are␊ |
144 | // roughly based on Linux code␊ |
145 | ␊ |
146 | /* Set the 8254 channel 2 to mode 0 with the specified value.␊ |
147 | In mode 0, the counter will initially set its gate low when the␊ |
148 | timer expires. For this to be useful, you ought to set it high␊ |
149 | before calling this function. The enable_PIT2 function does this.␊ |
150 | */␊ |
151 | static inline void set_PIT2_mode0(uint16_t value)␊ |
152 | {␊ |
153 | __asm__ volatile(␊ |
154 | ␉␉␉␉␉ " movb $0xB0,%%al \n\t"␊ |
155 | ␉␉␉␉␉ " outb␉%%al,$0x43␉\n\t"␊ |
156 | ␉␉␉␉␉ " movb␉%%dl,%%al␉\n\t"␊ |
157 | ␉␉␉␉␉ " outb␉%%al,$0x42␉\n\t"␊ |
158 | ␉␉␉␉␉ " movb␉%%dh,%%al␉\n\t"␊ |
159 | ␉␉␉␉␉ " outb␉%%al,$0x42"␊ |
160 | ␉␉␉␉␉ : : "d"(value) /*: no clobber */ );␊ |
161 | }␊ |
162 | ␊ |
163 | /* Returns the number of times the loop ran before the PIT2 signaled */␊ |
164 | static inline unsigned long poll_PIT2_gate(void)␊ |
165 | {␊ |
166 | unsigned long count = 0;␊ |
167 | unsigned char nmi_sc_val;␊ |
168 | do {␊ |
169 | ++count;␊ |
170 | __asm__ volatile(␊ |
171 | ␉␉␉␉␉␉ "inb␉$0x61,%0"␊ |
172 | ␉␉␉␉␉␉ : "=q"(nmi_sc_val) /*:*/ /* no input */ /*:*/ /* no clobber */);␊ |
173 | } while( (nmi_sc_val & 0x20) == 0);␊ |
174 | return count;␊ |
175 | }␊ |
176 | ␊ |
177 | inline static void␊ |
178 | set_PIT2(int value)␊ |
179 | {␊ |
180 | /*␊ |
181 | * First, tell the clock we are going to write 16 bits to the counter␊ |
182 | * and enable one-shot mode (command 0xB8 to port 0x43)␊ |
183 | * Then write the two bytes into the PIT2 clock register (port 0x42).␊ |
184 | * Loop until the value is "realized" in the clock,␊ |
185 | * this happens on the next tick.␊ |
186 | */␊ |
187 | asm volatile(␊ |
188 | " movb $0xB8,%%al \n\t"␊ |
189 | " outb %%al,$0x43 \n\t"␊ |
190 | " movb %%dl,%%al \n\t"␊ |
191 | " outb %%al,$0x42 \n\t"␊ |
192 | " movb %%dh,%%al \n\t"␊ |
193 | " outb %%al,$0x42 \n"␊ |
194 | "1: inb $0x42,%%al \n\t" ␊ |
195 | " inb $0x42,%%al \n\t"␊ |
196 | " cmp %%al,%%dh \n\t"␊ |
197 | " jne 1b"␊ |
198 | : : "d"(value) : "%al");␊ |
199 | }␊ |
200 | ␊ |
201 | ␊ |
202 | inline static uint64_t␊ |
203 | get_PIT2(unsigned int *value)␊ |
204 | {␊ |
205 | register uint64_t result;␊ |
206 | /*␊ |
207 | * This routine first latches the time (command 0x80 to port 0x43),␊ |
208 | * then gets the time stamp so we know how long the read will take later.␊ |
209 | * Read (from port 0x42) and return the current value of the timer.␊ |
210 | */␊ |
211 | #ifdef __i386__␊ |
212 | asm volatile(␊ |
213 | " xorl %%ecx,%%ecx \n\t"␊ |
214 | " movb $0x80,%%al \n\t"␊ |
215 | " outb %%al,$0x43 \n\t"␊ |
216 | " rdtsc \n\t"␊ |
217 | " pushl %%eax \n\t"␊ |
218 | " inb $0x42,%%al \n\t"␊ |
219 | " movb %%al,%%cl \n\t"␊ |
220 | " inb $0x42,%%al \n\t"␊ |
221 | " movb %%al,%%ch \n\t"␊ |
222 | " popl %%eax "␊ |
223 | : "=A"(result), "=c"(*value));␊ |
224 | #else /* __x86_64__ */␊ |
225 | asm volatile(␊ |
226 | ␉␉" xorq %%rcx,%%rcx \n\t"␊ |
227 | ␉␉" movb $0x80,%%al \n\t"␊ |
228 | ␉␉" outb %%al,$0x43 \n\t"␊ |
229 | ␉␉" rdtsc \n\t"␊ |
230 | ␉␉" pushq %%rax \n\t"␊ |
231 | ␉␉" inb $0x42,%%al \n\t"␊ |
232 | ␉␉" movb %%al,%%cl \n\t"␊ |
233 | ␉␉" inb $0x42,%%al \n\t"␊ |
234 | ␉␉" movb %%al,%%ch \n\t"␊ |
235 | ␉␉" popq %%rax "␊ |
236 | ␉␉: "=A"(result), "=c"(*value));␊ |
237 | #endif␊ |
238 | ␊ |
239 | return result;␊ |
240 | }␊ |
241 | ␊ |
242 | #endif /* !__LIBSAIO_CPU_H */␊ |
243 | |