Comment détecter si l'instruction CPUID est implémentée sur un
processeur i386 ?
Qu'est-ce que CPUID me direz-vous ?
CPUID est une instruction disponible sur les processeurs i386 et supérieur
(CISC).
A quoi sert-elle ?
Cette instruction permet de récupérer des informations sur votre processeur.
En effet, par le biais de cette instruction et via la commande mise dans le
registre %eax, nous pouvons récupérer diverses informations telle que
le type de processeur, possibilités (PAE, PSE, ...).
Bref des trucs bien sympatoches...
Mais dans un premier temps, il faut vérifier que cette instruction est
disponible sur le processeur. Pour cela il faut modifier le bit 21 du
registre EFLAGS ceci afin de voir si celui-ci est modifiable ou non.
Comment fait-on cela ?
Ainsi :
static int destect_cpuid_instr()
{
int result = -1;
/* 0x200000 = bit 21 */
asm volatile (
"pushf\n"
"popl %%eax\n"
"xorl $0x200000, %%eax\n"
"movl %%eax, %%ecx\n"
"andl $0x200000, %%ecx\n"
"pushl %%eax\n"
"popf\n"
"pushf\n"
"popl %%eax\n"
"andl $0x200000, %%eax\n"
"xorl %%eax, %%ecx\n"
"movl %%ecx, %0\n"
: "=r" (result) : : "eax", "ecx");
return (result == 0);
}
REMARQUE:
Le mémonique
PUSHFD et
PUSHF sont identiques au sens de GCC.
Néanmoins au sens Intel du terme, ils signifient :
- PUSHF: Empile les 16 bits de poids faibles de EFLAGS sur
la pile.
- PUSHFD: Empile complètement EFLAGS sur la pile (et donc
décrémente de 4 la pile)
En bref, GCC ne reconnait pas "pushfd" en tant que commande en assembleur
(asm();).
Bref,
On commence par empiler eflags[1], ensuite, on dépile dans le registre eax,
on fait un xor entre 0x200000 et eax (bref on modifie le bit 21 de
eflags).
Ensuite, on met le contenu de %eax dans %ecx, on fait un "et" de 0x200000
avec %ecx (Pour que dans %ecx, le bit 21 soit à "1" et le __reste__ à "0"
(plus facile pour tester)).
Puis, on empile %eax, puis on dépile le haut de la pile dans eflags et on
rempile. Si la modification apportée sur le bit 21 reste (0/1) alors CPUID
est une instruction valide sur ce processeur.
On dépile vers %eax, on fait un "et" entre 0x200000 et %eax, puis un xorl
entre %eax et %ecx. Et finalement, on place le contenu de %ecx dans %0 qui
correspond à "result" (: "=r" (result)).
En rapide, on stocke la valeur au début, puis on fait la modification du bit
21, puis on récupère eflags modifié dans %eax et on regarde si cela la
modification a été gardée ou non.
-----