On 09/12/2013 02:11 PM, Dridi Boukelmoune wrote:
This version should work in 32 bit mode, and only in 32 bit mode:
void
cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
unsigned int *edx)
{
__asm volatile
("push %%ebx\n\t"
"cpuid\n\t"
"mov %%ebx, (%1)\n\t"
"pop %%ebx"
: "=a" (*eax),
"=S" (ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax));
}
I "kind of" understand what you're doing here, but it's not all clear.
I get the push/pop instructions save and restore the reserved ebx
register, which is needed because apparently the cpuid instruction
would otherwise overwrite it.
I don't understand the mov instruction, but I suppose you're storing
ebx's value from cpuid "somewhere else" before restoring it with the
pop instruction.
Correct, the intent is to write the %ebx register value to the address
in the %esi register. "(%1)" is a pointer dereference, as oppose to
plain %1.
I don't understand the last 5 lines of __asm in both functions, I've
never seen this syntax before.
These are register constraints. "a", "c", "d", "S" refer to the %eax,
%ecx, %edx, %esi registers, respectively. "=" marks output constraints.
The constraints before the final ":" are output registers, and after
colon, there are the input registers. There's just one, and "0" means
to reuse the first output register.
Okay, silly me, I should have listed "S" among the output registers,
instead the inputs:
void
cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
unsigned int *edx)
{
__asm volatile
("push %%ebx\n\t"
"cpuid\n\t"
"mov %%ebx, (%4)\n\t"
"pop %%ebx"
: "=a" (*eax),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax),
"S" (ebx));
}
I also forget that for full correctness, there should now be a "memory"
clobber as well (in the clobber section after yet another colon):
void
cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
unsigned int *edx)
{
__asm volatile
("push %%ebx\n\t"
"cpuid\n\t"
"mov %%ebx, (%4)\n\t"
"pop %%ebx"
: "=a" (*eax),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax),
"S" (ebx)
: "memory");
}
By the way, we could generate much better code if the registers were
passed as an array or struct, so that they are in consecutive memory:
struct regs {
unsigned eax, ebx, ecx, edx;
};
void
cpuid(struct regs *r)
{
__asm volatile
("push %%ebx\n\t"
"cpuid\n\t"
"mov %%eax, (%0)\n\t"
"mov %%ebx, 4(%0)\n\t"
"mov %%ecx, 8(%0)\n\t"
"mov %%edx, 12(%0)\n\t"
"pop %%ebx"
:
: "S" (r)
: "eax", "ecx", "edx", "memory");
}
Obviously, this needs adjustments to the callers.
--
Florian Weimer / Red Hat Product Security Team
--
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxxx
https://admin.fedoraproject.org/mailman/listinfo/devel
Fedora Code of Conduct: http://fedoraproject.org/code-of-conduct