xref: /plan9-contrib/sys/src/9k/k10/archk10.c (revision 5ae61b61c8015e599b904d2af12b5be472f0d43e)
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