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)␉␉␉␉(1UL << (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 Typ"␊ |
18 | ␊ |
19 | #define MSR_CORE_THREAD_COUNT␉␉␉0x35␉␉␉// Undocumented␊ |
20 | #define MSR_EBC_FREQUENCY_ID␉␉␉0x2C␉␉␉// valv␊ |
21 | #define MSR_EBL_CR_POWERON␉␉␉␉0x2A␉␉␉// valv␊ |
22 | #define MSR_FLEX_RATIO␉␉␉␉␉0x194␊ |
23 | #define MSR_FSB_FREQ␉␉␉␉␉0xCD␉␉␉// valv␊ |
24 | #define MSR_IA32_CLOCK_MODULATION␉␉0x19A␉␉␉// valv␊ |
25 | #define MSR_IA32_EXT_CONFIG␉␉␉␉0xEE␊ |
26 | #define MSR_IA32_MISC_ENABLE␉␉␉0x1A0␉␉␉// valv␊ |
27 | #define MSR_IA32_PERF_CONTROL␉␉␉0x199␊ |
28 | #define␉MSR_IA32_PERF_STATUS␉␉␉0x198␊ |
29 | #define MSR_IA32_PLATFORM_ID␉␉␉0x17␉␉␉// valv␊ |
30 | #define␉MSR_PLATFORM_INFO␉␉␉␉0xCE␊ |
31 | #define MSR_THERMAL_STATUS␉␉␉␉0x19C␉␉␉// valv␊ |
32 | #define MSR_THERMAL_TARGET␉␉␉␉0x1A2␉␉␉// valv␊ |
33 | #define MSR_TURBO_RATIO_LIMIT␉␉␉0x1AD␉␉␉// valv␊ |
34 | #define MSR_MISC_PWR_MGMT␉␉␉␉0x1AA␉␉␉// valv␊ |
35 | ␊ |
36 | #define K8_FIDVID_STATUS␉␉␉␉0xC0010042␊ |
37 | #define K10_COFVID_STATUS␉␉␉␉0xC0010071␊ |
38 | #define␉MSR_AMD_10H_11H_LIMIT␉␉␉0xc0010061␉␉// valv␊ |
39 | #define␉AMD_10H_11H_CONFIG␉␉␉␉0xc0010064␉␉// valv␊ |
40 | ␊ |
41 | #define DEFAULT_FSB␉␉␉␉␉␉100000␉␉/* for now, hardcoding 100MHz for old CPUs */␊ |
42 | ␊ |
43 | // DFE: This constant comes from older xnu:␊ |
44 | #define CLKNUM␉␉␉␉␉␉␉1193182␉␉/* formerly 1193167 */␊ |
45 | ␊ |
46 | // DFE: These two constants come from Linux except CLOCK_TICK_RATE replaced with CLKNUM␊ |
47 | #define CALIBRATE_TIME_MSEC␉␉␉␉30␉␉/* 30 msecs */␊ |
48 | #define CALIBRATE_LATCH␉␉␉␉␉((CLKNUM * CALIBRATE_TIME_MSEC + 1000/2)/1000)␊ |
49 | ␊ |
50 | static inline uint64_t rdtsc64(void)␊ |
51 | {␊ |
52 | ␉uint64_t ret;␊ |
53 | ␉__asm__ volatile("rdtsc" : "=A" (ret));␊ |
54 | ␉return ret;␊ |
55 | }␊ |
56 | ␊ |
57 | static inline uint64_t rdmsr64(uint32_t msr)␊ |
58 | {␊ |
59 | uint64_t ret;␊ |
60 | __asm__ volatile("rdmsr" : "=A" (ret) : "c" (msr));␊ |
61 | return ret;␊ |
62 | }␊ |
63 | ␊ |
64 | static inline void wrmsr64(uint32_t msr, uint64_t val)␊ |
65 | {␊ |
66 | ␉__asm__ volatile("wrmsr" : : "c" (msr), "A" (val));␊ |
67 | }␊ |
68 | ␊ |
69 | typedef struct msr_struct␊ |
70 | {␊ |
71 | ␉unsigned lo;␊ |
72 | ␉unsigned hi;␊ |
73 | } msr_t;␊ |
74 | ␊ |
75 | static inline __attribute__((always_inline)) msr_t rdmsr(unsigned val)␊ |
76 | {␊ |
77 | ␉msr_t ret;␊ |
78 | ␉__asm__ volatile(␊ |
79 | ␉␉␉␉␉ "rdmsr"␊ |
80 | ␉␉␉␉␉ : "=a" (ret.lo), "=d" (ret.hi)␊ |
81 | ␉␉␉␉␉ : "c" (val)␊ |
82 | ␉␉␉␉␉ );␊ |
83 | ␉return ret;␊ |
84 | }␊ |
85 | ␊ |
86 | static inline __attribute__((always_inline)) void wrmsr(unsigned val, msr_t msr)␊ |
87 | {␊ |
88 | ␉__asm__ __volatile__ (␊ |
89 | ␉␉␉␉␉␉ "wrmsr"␊ |
90 | ␉␉␉␉␉␉ : /* No outputs */␊ |
91 | ␉␉␉␉␉␉ : "c" (val), "a" (msr.lo), "d" (msr.hi)␊ |
92 | ␉␉␉␉␉␉ );␊ |
93 | }␊ |
94 | ␊ |
95 | static inline void intel_waitforsts(void) {␊ |
96 | ␉uint32_t inline_timeout = 100000;␊ |
97 | ␉while (rdmsr64(MSR_IA32_PERF_STATUS) & (1 << 21)) { if (!inline_timeout--) break; }␊ |
98 | }␊ |
99 | ␊ |
100 | static inline void do_cpuid(uint32_t selector, uint32_t *data)␊ |
101 | {␊ |
102 | ␉asm volatile ("cpuid"␊ |
103 | ␉␉␉␉ : "=a" (data[0]),␊ |
104 | ␉␉␉␉ "=b" (data[1]),␊ |
105 | ␉␉␉␉ "=c" (data[2]),␊ |
106 | ␉␉␉␉ "=d" (data[3])␊ |
107 | ␉␉␉␉ : "a" (selector));␊ |
108 | }␊ |
109 | ␊ |
110 | static inline void do_cpuid2(uint32_t selector, uint32_t selector2, uint32_t *data)␊ |
111 | {␊ |
112 | ␉asm volatile ("cpuid"␊ |
113 | ␉␉␉␉ : "=a" (data[0]),␊ |
114 | ␉␉␉␉ "=b" (data[1]),␊ |
115 | ␉␉␉␉ "=c" (data[2]),␊ |
116 | ␉␉␉␉ "=d" (data[3])␊ |
117 | ␉␉␉␉ : "a" (selector), "c" (selector2));␊ |
118 | }␊ |
119 | ␊ |
120 | // DFE: enable_PIT2 and disable_PIT2 come from older xnu␊ |
121 | ␊ |
122 | /*␊ |
123 | * Enable or disable timer 2.␊ |
124 | * Port 0x61 controls timer 2:␊ |
125 | * bit 0 gates the clock,␊ |
126 | * bit 1 gates output to speaker.␊ |
127 | */␊ |
128 | static inline void enable_PIT2(void)␊ |
129 | {␊ |
130 | /* Enable gate, disable speaker */␊ |
131 | __asm__ volatile(␊ |
132 | ␉␉␉␉␉ " inb $0x61,%%al \n\t"␊ |
133 | ␉␉␉␉␉ " and $0xFC,%%al \n\t" /* & ~0x03 */␊ |
134 | ␉␉␉␉␉ " or $1,%%al \n\t"␊ |
135 | ␉␉␉␉␉ " outb %%al,$0x61 \n\t"␊ |
136 | ␉␉␉␉␉ : : : "%al" );␊ |
137 | }␊ |
138 | ␊ |
139 | static inline void disable_PIT2(void)␊ |
140 | {␊ |
141 | /* Disable gate and output to speaker */␊ |
142 | __asm__ volatile(␊ |
143 | ␉␉␉␉␉ " inb $0x61,%%al \n\t"␊ |
144 | ␉␉␉␉␉ " and $0xFC,%%al \n\t"␉/* & ~0x03 */␊ |
145 | ␉␉␉␉␉ " outb %%al,$0x61 \n\t"␊ |
146 | ␉␉␉␉␉ : : : "%al" );␊ |
147 | }␊ |
148 | ␊ |
149 | // DFE: set_PIT2_mode0, poll_PIT2_gate, and measure_tsc_frequency are␊ |
150 | // roughly based on Linux code␊ |
151 | ␊ |
152 | /* Set the 8254 channel 2 to mode 0 with the specified value.␊ |
153 | In mode 0, the counter will initially set its gate low when the␊ |
154 | timer expires. For this to be useful, you ought to set it high␊ |
155 | before calling this function. The enable_PIT2 function does this.␊ |
156 | */␊ |
157 | static inline void set_PIT2_mode0(uint16_t value)␊ |
158 | {␊ |
159 | __asm__ volatile(␊ |
160 | ␉␉␉␉␉ " movb $0xB0,%%al \n\t"␊ |
161 | ␉␉␉␉␉ " outb␉%%al,$0x43 \n\t"␊ |
162 | ␉␉␉␉␉ " movb␉%%dl,%%al \n\t"␊ |
163 | ␉␉␉␉␉ " outb␉%%al,$0x42 \n\t"␊ |
164 | ␉␉␉␉␉ " movb␉%%dh,%%al \n\t"␊ |
165 | ␉␉␉␉␉ " outb␉%%al,$0x42"␊ |
166 | ␉␉␉␉␉ : : "d"(value) /*: no clobber */ );␊ |
167 | }␊ |
168 | ␊ |
169 | /* Returns the number of times the loop ran before the PIT2 signaled */␊ |
170 | static inline unsigned long poll_PIT2_gate(void)␊ |
171 | {␊ |
172 | unsigned long count = 0;␊ |
173 | unsigned char nmi_sc_val;␊ |
174 | do {␊ |
175 | ++count;␊ |
176 | __asm__ volatile(␊ |
177 | ␉␉␉␉␉␉ "inb␉$0x61,%0"␊ |
178 | ␉␉␉␉␉␉ : "=q"(nmi_sc_val) /*:*/ /* no input */ /*:*/ /* no clobber */);␊ |
179 | } while( (nmi_sc_val & 0x20) == 0);␊ |
180 | return count;␊ |
181 | }␊ |
182 | ␊ |
183 | #endif /* !__LIBSAIO_CPU_H */␊ |
184 | |