décembre 20, 2006 Archives

mer déc 20 21:13:46 CET 2006

i386 et CPUID

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.

-----