core: do aligned transfers in bcopy32
[syslinux.git] / com32 / modules / cpuid.c
blobe11ac717b8bf1000959cfd38f6284bb44205d5c2
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2006 Erwan Velu - All Rights Reserved
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
12 * conditions:
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * -----------------------------------------------------------------------
29 #include <stdio.h>
30 #include <string.h>
31 #include "cpuid.h"
33 struct cpu_dev * cpu_devs[X86_VENDOR_NUM] = {};
36 * CPUID functions returning a single datum
38 static inline unsigned int cpuid_eax(unsigned int op)
40 unsigned int eax;
42 __asm__("cpuid"
43 : "=a" (eax)
44 : "0" (op)
45 : "bx", "cx", "dx");
46 return eax;
49 static inline unsigned int cpuid_ecx(unsigned int op)
51 unsigned int eax, ecx;
53 __asm__("cpuid"
54 : "=a" (eax), "=c" (ecx)
55 : "0" (op)
56 : "bx", "dx" );
57 return ecx;
59 static inline unsigned int cpuid_edx(unsigned int op)
61 unsigned int eax, edx;
63 __asm__("cpuid"
64 : "=a" (eax), "=d" (edx)
65 : "0" (op)
66 : "bx", "cx");
67 return edx;
70 /* Standard macro to see if a specific flag is changeable */
71 static inline int flag_is_changeable_p(u32 flag)
73 u32 f1, f2;
75 asm("pushfl\n\t"
76 "pushfl\n\t"
77 "popl %0\n\t"
78 "movl %0,%1\n\t"
79 "xorl %2,%0\n\t"
80 "pushl %0\n\t"
81 "popfl\n\t"
82 "pushfl\n\t"
83 "popl %0\n\t"
84 "popfl\n\t"
85 : "=&r" (f1), "=&r" (f2)
86 : "ir" (flag));
88 return ((f1^f2) & flag) != 0;
91 /* Probe for the CPUID instruction */
92 static int have_cpuid_p(void)
94 return flag_is_changeable_p(X86_EFLAGS_ID);
97 static struct cpu_dev amd_cpu_dev = {
98 .c_vendor = "AMD",
99 .c_ident = { "AuthenticAMD" }
102 static struct cpu_dev intel_cpu_dev = {
103 .c_vendor = "Intel",
104 .c_ident = { "GenuineIntel" }
107 static struct cpu_dev cyrix_cpu_dev = {
108 .c_vendor = "Cyrix",
109 .c_ident = { "CyrixInstead" }
112 static struct cpu_dev umc_cpu_dev = {
113 .c_vendor = "UMC",
114 .c_ident = { "UMC UMC UMC" }
118 static struct cpu_dev nexgen_cpu_dev = {
119 .c_vendor = "Nexgen",
120 .c_ident = { "NexGenDriven" }
123 static struct cpu_dev centaur_cpu_dev = {
124 .c_vendor = "Centaur",
125 .c_ident = { "CentaurHauls" }
128 static struct cpu_dev rise_cpu_dev = {
129 .c_vendor = "Rise",
130 .c_ident = { "RiseRiseRise" }
133 static struct cpu_dev transmeta_cpu_dev = {
134 .c_vendor = "Transmeta",
135 .c_ident = { "GenuineTMx86", "TransmetaCPU" }
138 void init_cpu_devs(void)
140 cpu_devs[X86_VENDOR_INTEL] = &intel_cpu_dev;
141 cpu_devs[X86_VENDOR_CYRIX] = &cyrix_cpu_dev;
142 cpu_devs[X86_VENDOR_AMD] = &amd_cpu_dev;
143 cpu_devs[X86_VENDOR_UMC] = &umc_cpu_dev;
144 cpu_devs[X86_VENDOR_NEXGEN] = &nexgen_cpu_dev;
145 cpu_devs[X86_VENDOR_CENTAUR] = &centaur_cpu_dev;
146 cpu_devs[X86_VENDOR_RISE] = &rise_cpu_dev;
147 cpu_devs[X86_VENDOR_TRANSMETA] = &transmeta_cpu_dev;
150 void get_cpu_vendor(struct cpuinfo_x86 *c)
152 char *v = c->x86_vendor_id;
153 int i;
154 init_cpu_devs();
155 for (i = 0; i < X86_VENDOR_NUM; i++) {
156 if (cpu_devs[i]) {
157 if (!strcmp(v,cpu_devs[i]->c_ident[0]) ||
158 (cpu_devs[i]->c_ident[1] &&
159 !strcmp(v,cpu_devs[i]->c_ident[1]))) {
160 c->x86_vendor = i;
161 return;
166 c->x86_vendor = X86_VENDOR_UNKNOWN;
169 int get_model_name(struct cpuinfo_x86 *c)
171 unsigned int *v;
172 char *p, *q;
174 if (cpuid_eax(0x80000000) < 0x80000004)
175 return 0;
177 v = (unsigned int *) c->x86_model_id;
178 cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
179 cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
180 cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
181 c->x86_model_id[48] = 0;
183 /* Intel chips right-justify this string for some dumb reason;
184 undo that brain damage */
185 p = q = &c->x86_model_id[0];
186 while ( *p == ' ' )
187 p++;
188 if ( p != q ) {
189 while ( *p )
190 *q++ = *p++;
191 while ( q <= &c->x86_model_id[48] )
192 *q++ = '\0'; /* Zero-pad the rest */
195 return 1;
198 void generic_identify(struct cpuinfo_x86 *c)
200 u32 tfms, xlvl;
201 int junk;
202 /* Get vendor name */
203 cpuid(0x00000000, &c->cpuid_level,
204 (int *)&c->x86_vendor_id[0],
205 (int *)&c->x86_vendor_id[8],
206 (int *)&c->x86_vendor_id[4]);
208 get_cpu_vendor(c);
209 /* Intel-defined flags: level 0x00000001 */
210 if ( c->cpuid_level >= 0x00000001 ) {
211 u32 capability, excap;
212 cpuid(0x00000001, &tfms, &junk, &excap, &capability);
213 c->x86_capability[0] = capability;
214 c->x86_capability[4] = excap;
215 c->x86 = (tfms >> 8) & 15;
216 c->x86_model = (tfms >> 4) & 15;
217 if (c->x86 == 0xf) {
218 c->x86 += (tfms >> 20) & 0xff;
219 c->x86_model += ((tfms >> 16) & 0xF) << 4;
221 c->x86_mask = tfms & 15;
222 if (capability & (1<<19))
223 c->x86_cache_alignment = ((junk >> 8) & 0xff) * 8;
224 } else {
225 /* Have CPUID level 0 only - unheard of */
226 c->x86 = 4;
229 /* AMD-defined flags: level 0x80000001 */
230 xlvl = cpuid_eax(0x80000000);
231 if ( (xlvl & 0xffff0000) == 0x80000000 ) {
232 if ( xlvl >= 0x80000001 ) {
233 c->x86_capability[1] = cpuid_edx(0x80000001);
234 c->x86_capability[6] = cpuid_ecx(0x80000001);
236 if ( xlvl >= 0x80000004 )
237 get_model_name(c); /* Default name */
242 * Checksum an MP configuration block.
245 static int mpf_checksum(unsigned char *mp, int len)
247 int sum = 0;
249 while (len--)
250 sum += *mp++;
252 return sum & 0xFF;
255 static int smp_scan_config (unsigned long base, unsigned long length)
257 unsigned long *bp = base;
258 struct intel_mp_floating *mpf;
260 // printf("Scan SMP from %p for %ld bytes.\n", bp,length);
261 if (sizeof(*mpf) != 16) {
262 printf("Error: MPF size\n");
263 return 0;
266 while (length > 0) {
267 mpf = (struct intel_mp_floating *)bp;
268 if ((*bp == SMP_MAGIC_IDENT) &&
269 (mpf->mpf_length == 1) &&
270 !mpf_checksum((unsigned char *)bp, 16) &&
271 ((mpf->mpf_specification == 1)
272 || (mpf->mpf_specification == 4)) ) {
273 return 1;
275 bp += 4;
276 length -= 16;
278 return 0;
281 int find_smp_config (void)
283 // unsigned int address;
286 * FIXME: Linux assumes you have 640K of base ram..
287 * this continues the error...
289 * 1) Scan the bottom 1K for a signature
290 * 2) Scan the top 1K of base RAM
291 * 3) Scan the 64K of bios
293 if (smp_scan_config(0x0,0x400) ||
294 smp_scan_config(639*0x400,0x400) ||
295 smp_scan_config(0xF0000,0x10000))
296 return 1;
298 * If it is an SMP machine we should know now, unless the
299 * configuration is in an EISA/MCA bus machine with an
300 * extended bios data area.
302 * there is a real-mode segmented pointer pointing to the
303 * 4K EBDA area at 0x40E, calculate and scan it here.
305 * NOTE! There are Linux loaders that will corrupt the EBDA
306 * area, and as such this kind of SMP config may be less
307 * trustworthy, simply because the SMP table may have been
308 * stomped on during early boot. These loaders are buggy and
309 * should be fixed.
311 * MP1.4 SPEC states to only scan first 1K of 4K EBDA.
314 // address = get_bios_ebda();
315 // if (address)
316 // smp_scan_config(address, 0x400);
317 return 0;
321 void set_cpu_flags(struct cpuinfo_x86 *c, s_cpu *cpu) {
322 cpu->flags.fpu=cpu_has(c, X86_FEATURE_FPU);
323 cpu->flags.vme=cpu_has(c, X86_FEATURE_VME);
324 cpu->flags.de=cpu_has(c, X86_FEATURE_DE);
325 cpu->flags.pse=cpu_has(c, X86_FEATURE_PSE);
326 cpu->flags.tsc=cpu_has(c, X86_FEATURE_TSC);
327 cpu->flags.msr=cpu_has(c, X86_FEATURE_MSR);
328 cpu->flags.pae=cpu_has(c, X86_FEATURE_PAE);
329 cpu->flags.mce=cpu_has(c, X86_FEATURE_MCE);
330 cpu->flags.cx8=cpu_has(c, X86_FEATURE_CX8);
331 cpu->flags.apic=cpu_has(c, X86_FEATURE_APIC);
332 cpu->flags.sep=cpu_has(c, X86_FEATURE_SEP);
333 cpu->flags.mtrr=cpu_has(c, X86_FEATURE_MTRR);
334 cpu->flags.pge=cpu_has(c, X86_FEATURE_PGE);
335 cpu->flags.mca=cpu_has(c, X86_FEATURE_MCA);
336 cpu->flags.cmov=cpu_has(c, X86_FEATURE_CMOV);
337 cpu->flags.pat=cpu_has(c, X86_FEATURE_PAT);
338 cpu->flags.pse_36=cpu_has(c, X86_FEATURE_PSE36);
339 cpu->flags.psn=cpu_has(c, X86_FEATURE_PN);
340 cpu->flags.clflsh=cpu_has(c, X86_FEATURE_CLFLSH);
341 cpu->flags.dts=cpu_has(c, X86_FEATURE_DTES);
342 cpu->flags.acpi=cpu_has(c, X86_FEATURE_ACPI);
343 cpu->flags.mmx=cpu_has(c, X86_FEATURE_MMX);
344 cpu->flags.fxsr=cpu_has(c, X86_FEATURE_FXSR);
345 cpu->flags.sse=cpu_has(c, X86_FEATURE_XMM);
346 cpu->flags.sse2=cpu_has(c, X86_FEATURE_XMM2);
347 cpu->flags.ss=cpu_has(c, X86_FEATURE_SELFSNOOP);
348 cpu->flags.htt=cpu_has(c, X86_FEATURE_HT);
349 cpu->flags.acc=cpu_has(c, X86_FEATURE_ACC);
350 cpu->flags.syscall=cpu_has(c, X86_FEATURE_SYSCALL);
351 cpu->flags.mp=cpu_has(c, X86_FEATURE_MP);
352 cpu->flags.nx=cpu_has(c, X86_FEATURE_NX);
353 cpu->flags.mmxext=cpu_has(c, X86_FEATURE_MMXEXT);
354 cpu->flags.lm=cpu_has(c, X86_FEATURE_LM);
355 cpu->flags.nowext=cpu_has(c, X86_FEATURE_3DNOWEXT);
356 cpu->flags.now=cpu_has(c, X86_FEATURE_3DNOW);
357 cpu->flags.smp = find_smp_config();
360 void set_generic_info(struct cpuinfo_x86 *c,s_cpu *cpu) {
361 cpu->family=c->x86;
362 cpu->vendor_id=c->x86_vendor;
363 cpu->model_id=c->x86_model;
364 cpu->stepping=c->x86_mask;
365 strncpy(cpu->vendor,cpu_devs[c->x86_vendor]->c_vendor,CPU_VENDOR_SIZE);
366 strncpy(cpu->model,c->x86_model_id,CPU_MODEL_SIZE);
369 void detect_cpu(s_cpu *cpu)
371 struct cpuinfo_x86 c;
372 c.x86_cache_alignment = 32;
373 c.x86_cache_size = -1;
374 c.x86_vendor = X86_VENDOR_UNKNOWN;
375 c.cpuid_level = -1; /* CPUID not detected */
376 c.x86_model = c.x86_mask = 0; /* So far unknown... */
377 c.x86_vendor_id[0] = '\0'; /* Unset */
378 c.x86_model_id[0] = '\0'; /* Unset */
379 memset(&c.x86_vendor_id,'\0',CPU_VENDOR_SIZE);
381 if (!have_cpuid_p())
382 return;
384 generic_identify(&c);
385 set_generic_info(&c,cpu);
386 set_cpu_flags(&c,cpu);