1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6
7 static int
portwaitfor(int * vp,int val)8 portwaitfor(int *vp, int val)
9 {
10 int i;
11
12 /*
13 * How many times round this loop?
14 */
15 for(i = 0; *vp == val && i < 10000; i++)
16 ;
17
18 return *vp;
19 }
20
21 int (*waitfor)(int*, int) = portwaitfor;
22
23 extern int k10waitfor(int*, int);
24
25 static int
cpuidinit(void)26 cpuidinit(void)
27 {
28 u32int eax, info[4];
29
30 /*
31 * Standard CPUID functions.
32 * Functions 0 and 1 will be needed multiple times
33 * so cache the info now.
34 */
35 if((m->ncpuinfos = cpuid(0, 0, m->cpuinfo[0])) == 0)
36 return 0;
37 m->ncpuinfos++;
38
39 if(memcmp(&m->cpuinfo[0][1], "GenuntelineI", 12) == 0)
40 m->isintelcpu = 1;
41 cpuid(1, 0, m->cpuinfo[1]);
42
43 /*
44 * Extended CPUID functions.
45 */
46 if((eax = cpuid(0x80000000, 0, info)) >= 0x80000000)
47 m->ncpuinfoe = (eax & ~0x80000000) + 1;
48
49 /*
50 * Is MONITOR/MWAIT supported?
51 */
52 if(m->cpuinfo[1][2] & 8){
53 /*
54 * Will be interested in parameters,
55 * extensions, and hints later; they can be retrieved
56 * with standard CPUID function 5.
57 */
58 waitfor = k10waitfor;
59 }
60
61 return 1;
62 }
63
64 static int
cpuidinfo(u32int eax,u32int ecx,u32int info[4])65 cpuidinfo(u32int eax, u32int ecx, u32int info[4])
66 {
67 if(m->ncpuinfos == 0 && cpuidinit() == 0)
68 return 0;
69
70 if(!(eax & 0x80000000)){
71 if(eax >= m->ncpuinfos)
72 return 0;
73 }
74 else if(eax >= (0x80000000|m->ncpuinfoe))
75 return 0;
76
77 cpuid(eax, ecx, info);
78
79 return 1;
80 }
81
82 static vlong
cpuidhz(u32int info[2][4])83 cpuidhz(u32int info[2][4])
84 {
85 int f, r;
86 vlong hz;
87 u64int msr;
88
89 if(memcmp(&info[0][1], "GenuntelineI", 12) == 0){
90 switch(info[1][0] & 0x0fff3ff0){
91 default:
92 return 0;
93 case 0x00000f30: /* Xeon (MP), Pentium [4D] */
94 case 0x00000f40: /* Xeon (MP), Pentium [4D] */
95 case 0x00000f60: /* Xeon 7100, 5000 or above */
96 msr = rdmsr(0x2c);
97 r = (msr>>16) & 0x07;
98 switch(r){
99 default:
100 return 0;
101 case 0:
102 hz = 266666666666ll;
103 break;
104 case 1:
105 hz = 133333333333ll;
106 break;
107 case 2:
108 hz = 200000000000ll;
109 break;
110 case 3:
111 hz = 166666666666ll;
112 break;
113 case 4:
114 hz = 333333333333ll;
115 break;
116 }
117
118 /*
119 * Hz is *1000 at this point.
120 * Do the scaling then round it.
121 * The manual is conflicting about
122 * the size of the msr field.
123 */
124 hz = (((hz*(msr>>24))/100)+5)/10;
125 break;
126 case 0x00000690: /* Pentium M, Celeron M */
127 case 0x000006d0: /* Pentium M, Celeron M */
128 hz = ((rdmsr(0x2a)>>22) & 0x1f)*100 * 1000000ll;
129 break;
130 case 0x000006e0: /* Core Duo */
131 case 0x000006f0: /* Core 2 Duo/Quad/Extreme */
132 case 0x00010670: /* Core 2 Extreme */
133 /*
134 * Get the FSB frequemcy.
135 * If processor has Enhanced Intel Speedstep Technology
136 * then non-integer bus frequency ratios are possible.
137 */
138 if(info[1][2] & 0x00000080){
139 msr = rdmsr(0x198);
140 r = (msr>>40) & 0x1f;
141 }
142 else{
143 msr = 0;
144 r = rdmsr(0x2a) & 0x1f;
145 }
146 f = rdmsr(0xcd) & 0x07;
147 switch(f){
148 default:
149 return 0;
150 case 5:
151 hz = 100000000000ll;
152 break;
153 case 1:
154 hz = 133333333333ll;
155 break;
156 case 3:
157 hz = 166666666666ll;
158 break;
159 case 2:
160 hz = 200000000000ll;
161 break;
162 case 0:
163 hz = 266666666666ll;
164 break;
165 case 4:
166 hz = 333333333333ll;
167 break;
168 case 6:
169 hz = 400000000000ll;
170 break;
171 }
172
173 /*
174 * Hz is *1000 at this point.
175 * Do the scaling then round it.
176 */
177 if(msr & 0x0000400000000000ll)
178 hz = hz*r + hz/2;
179 else
180 hz = hz*r;
181 hz = ((hz/100)+5)/10;
182 break;
183 }
184 DBG("cpuidhz: 0x2a: %#llux hz %lld\n", rdmsr(0x2a), hz);
185 }
186 else if(memcmp(&info[0][1], "AuthcAMDenti", 12) == 0){
187 switch(info[1][0] & 0x0fff0ff0){
188 default:
189 return 0;
190 case 0x00000f50: /* K8 */
191 msr = rdmsr(0xc0010042);
192 if(msr == 0)
193 return 0;
194 hz = (800 + 200*((msr>>1) & 0x1f)) * 1000000ll;
195 break;
196 case 0x00100f90: /* K10 */
197 case 0x00000620: /* QEMU64 */
198 msr = rdmsr(0xc0010064);
199 r = (msr>>6) & 0x07;
200 hz = (((msr & 0x3f)+0x10)*100000000ll)/(1<<r);
201 break;
202 }
203 DBG("cpuidhz: %#llux hz %lld\n", msr, hz);
204 }
205 else
206 return 0;
207
208 return hz;
209 }
210
211 void
cpuiddump(void)212 cpuiddump(void)
213 {
214 int i;
215 u32int info[4];
216
217 if(!DBGFLG)
218 return;
219
220 if(m->ncpuinfos == 0 && cpuidinit() == 0)
221 return;
222
223 for(i = 0; i < m->ncpuinfos; i++){
224 cpuid(i, 0, info);
225 DBG("eax = %#8.8ux: %8.8ux %8.8ux %8.8ux %8.8ux\n",
226 i, info[0], info[1], info[2], info[3]);
227 }
228 for(i = 0; i < m->ncpuinfoe; i++){
229 cpuid(0x80000000|i, 0, info);
230 DBG("eax = %#8.8ux: %8.8ux %8.8ux %8.8ux %8.8ux\n",
231 0x80000000|i, info[0], info[1], info[2], info[3]);
232 }
233 }
234
235 vlong
archhz(void)236 archhz(void)
237 {
238 vlong hz;
239 u32int info[2][4];
240
241 if(DBGFLG && m->machno == 0)
242 cpuiddump();
243 if(!cpuidinfo(0, 0, info[0]) || !cpuidinfo(1, 0, info[1]))
244 return 0;
245
246 hz = cpuidhz(info);
247 if(hz != 0)
248 return hz;
249 else if(m->machno != 0)
250 return sys->machptr[0]->cpuhz;
251
252 return i8254hz(info);
253 }
254
255 int
archmmu(void)256 archmmu(void)
257 {
258 u32int info[4];
259
260 /*
261 * Should the check for m->machno != 0 be here
262 * or in the caller (mmuinit)?
263 *
264 * To do here:
265 * check and enable Pse;
266 * Pge; Nxe.
267 */
268
269 /*
270 * How many page sizes are there?
271 * Always have 4*KiB, but need to check
272 * configured correctly.
273 */
274 assert(PGSZ == 4*KiB);
275
276 m->pgszlg2[0] = 12;
277 m->pgszmask[0] = (1<<12)-1;
278 m->npgsz = 1;
279 if(m->ncpuinfos == 0 && cpuidinit() == 0)
280 return 1;
281
282 /*
283 * Check the Pse bit in function 1 DX for 2*MiB support;
284 * if false, only 4*KiB is available.
285 */
286 if(!(m->cpuinfo[1][3] & 0x00000008))
287 return 1;
288 m->pgszlg2[1] = 21;
289 m->pgszmask[1] = (1<<21)-1;
290 m->npgsz = 2;
291
292 /*
293 * Check the Page1GB bit in function 0x80000001 DX for 1*GiB support.
294 */
295 if(cpuidinfo(0x80000001, 0, info) && (info[3] & 0x04000000)){
296 m->pgszlg2[2] = 30;
297 m->pgszmask[2] = (1<<30)-1;
298 m->npgsz = 3;
299 }
300
301 return m->npgsz;
302 }
303
304 static int
fmtP(Fmt * f)305 fmtP(Fmt* f)
306 {
307 uintmem pa;
308
309 pa = va_arg(f->args, uintmem);
310
311 if(f->flags & FmtSharp)
312 return fmtprint(f, "%#16.16llux", pa);
313
314 return fmtprint(f, "%llud", pa);
315 }
316
317 static int
fmtL(Fmt * f)318 fmtL(Fmt* f)
319 {
320 Mpl pl;
321
322 pl = va_arg(f->args, Mpl);
323
324 return fmtprint(f, "%#16.16llux", pl);
325 }
326
327 static int
fmtR(Fmt * f)328 fmtR(Fmt* f)
329 {
330 u64int r;
331
332 r = va_arg(f->args, u64int);
333
334 return fmtprint(f, "%#16.16llux", r);
335 }
336
337 void
archfmtinstall(void)338 archfmtinstall(void)
339 {
340 /*
341 * Architecture-specific formatting. Not as neat as they
342 * could be (e.g. there's no defined type for a 'register':
343 * L - Mpl, mach priority level
344 * P - uintmem, physical address
345 * R - register
346 * With a little effort these routines could be written
347 * in a fairly architecturally-independent manner, relying
348 * on the compiler to optimise-away impossible conditions,
349 * and/or by exploiting the innards of the fmt library.
350 */
351 fmtinstall('P', fmtP);
352
353 fmtinstall('L', fmtL);
354 fmtinstall('R', fmtR);
355 }
356
357 void
microdelay(int microsecs)358 microdelay(int microsecs)
359 {
360 u64int r, t;
361
362 r = rdtsc();
363 for(t = r + m->cpumhz*microsecs; r < t; r = rdtsc())
364 pause();
365 }
366
367 void
millidelay(int millisecs)368 millidelay(int millisecs)
369 {
370 u64int r, t;
371
372 r = rdtsc();
373 for(t = r + m->cpumhz*1000ull*millisecs; r < t; r = rdtsc())
374 pause();
375 }
376
377 int
isdmaok(void * a,usize len,int range)378 isdmaok(void *a, usize len, int range)
379 {
380 uintmem pa;
381
382 if(!iskaddr(a) || (char*)a < etext)
383 return 0;
384 pa = mmuphysaddr(PTR2UINT(a));
385 if(pa == 0 || pa == ~(uintmem)0)
386 return 0;
387 return range > 32 || pa+len <= 0xFFFFFFFFULL;
388 }
389