[ccan] [PATCH] cpuid: new module

Rusty Russell rusty at rustcorp.com.au
Wed Sep 25 15:08:59 EST 2013


Ahmed Samy <f.fallen45 at gmail.com> writes:
> This module parses data provided by the cpuid instruction.
> It still needs more work, however, it works for most important
> stuff.
>
> Signed-off-by: Ahmed Samy <f.fallen45 at gmail.com>

Hmm, the .S file is problematic.  We haven't had one before.

The rule for compiling a CCAN module is: compile *.c.

So, it would be best to put that function inline.  I can't see anything
on Microsoft's web site about handling pre-cpuid CPUs (and __cpuid was
introduced in 1998, so perhaps it was a requirement for Windows at that
point (cpuid was introduced in 1993).

So, perhaps make cpuid_is_supported() 1 on Windows, and use inline asm?

Minor nits below:
> +#ifndef CCAN_CPUID_H
> +#define CCAN_CPUID_H
> +
> +typedef enum cpuid {

Not a fan of gratuitous typedefs, but I know others like them.  I find
'enum cpuid' more explicit than 'cpuid_t'.

> +/* returns 1 if the cpuid instruction is supported, 0 otherwise.
> + *
> + * CPUID isn't supported on very old Intel CPUs.
> + * Defined in issupprted.S
> + */
> +int cpuid_is_supported(void);

I assume stdbool.h exists on Windows?  We should use it...

> +/* returns the highest extended function supported.
> + *
> + * This is the same as calling:
> + * 	cpuid(CPU_HIGHEST_EEXTENDED_FUNCTION_SUPPORTED, &highest);
> + *
> + * This is made visible to the linker because it's easier to call it
> + * instead of calling cpuid with less type-checking.  cpuid calls this.
> + */
> +int highest_ext_func_supported(void);

Why int, not uint32_t?

> +/* Get Some information from the CPU.
> + * This function expects buf to be a valid pointer to a string/int/...
> + * depending on the requested information.
> + *
> + * For CPU_VENDOR_ID:
> + * 	Returns a string into buf.
> + *
> + * For CPU_PROCINFO_AND_FEATUREBITS:
> + * 	buf[0]:
> + * 		- 3:0 - Stepping
> + * 		- 7:4 - Model
> + * 		- 11:8 - Family
> + * 		- 13:12 - Processor Type
> + * 		- 19:16 - Extended Model
> + * 		- 27:20 - Extended family
> + * 	buf[1] and buf[2]:
> + * 		Feature flags
> + * 	buf[3]:
> + * 		Additional feature information.
> + *
> + * For CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED:
> + * 	Returns the highest supported function in *buf (expects an integer ofc)
> + *
> + * For CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
> + * 	Returns them in buf[0] and buf[1].
> + *
> + * For CPU_VIRT_PHYS_ADDR_SIZES:
> + * 	Returns it as an integer in *buf.
> + *
> + * For CPU_PROC_BRAND_STRING:
> + * 	Have a char array with at least 48 bytes assigned to it.
> + *
> + * If an invalid flag has been passed a 0xbaadf00d is returned in *buf.

You mean "any other info value" right?

> + */
> +void cpuid(cpuid_t info, void *buf);

But buf is always a uint32_t array.  Maybe change to that, and create a
simple wrapper, eg:

static inline void cpuid_vendor_id(char id[48])
{
        cpuid(CPU_VENDOR_ID, (uint32_t *)id);
}

> +/*
> + * Returns 1 if feature is supported, 0 otherwise.
> + *
> + * The feature parameter must be >= CPU_EXTENDED_PROC_INFO_FEATURE_BITS
> + *  and <= CPU_VIRT_PHYS_ADDR_SIZES.
> + */
> +int cpuid_test_feature(cpuid_t feature);

bool again...

> +
> +/* Test if the CPU supports MMX/SSE*
> + *
> + * Returns 1 if the feature is available, 0 otherwise.
> + */
> +#define cpuid_has_mmx() 	cpuid_has_feature(CF_MMX)
> +#define cpuid_has_sse() 	cpuid_has_feature(CF_SSE)
> +#define cpuid_has_sse2() 	cpuid_has_feature(CF_SSE2)
> +#define cpuid_has_sse3() 	cpuid_has_feature(CF_SSE3)
> +#define cpuid_has_ssse3() 	cpuid_has_feature(CF_SSSE3)
> +#define cpuid_has_sse41() 	cpuid_has_feature(CF_SSE41)
> +#define cpuid_has_sse42() 	cpuid_has_feature(CF_SSE42)
> +#define cpuid_has_avx() 	cpuid_has_feature(CF_AVX)
> +#define cpuid_has_fma() 	cpuid_has_feature(CF_FMA)
> +int cpuid_has_feature(cpufeature_t feature);
> +
> +/* Test if the CPU supports an extended feature.
> + *
> + * Returns 1 if available, 0 otherwise.
> + */
> +#define cpuid_has_x64() 	cpuid_has_ext_feature(CEF_x64)
> +#define cpuid_has_sse4a() 	cpuid_has_ext_feature(CEF_SSE4a)
> +#define cpuid_has_fma4() 	cpuid_has_ext_feature(CEF_FMA4)
> +#define cpuid_has_xop() 	cpuid_has_ext_feature(CEF_XOP)
> +int cpuid_has_ext_feature(cpuextfeature_t extfeature);

The rest looks fine, though as David suggested, using ccan/build_assert
would be nice, so you can create dummy macros for non-x86 which compile
but die if they're ever called, eg:

#else /* non-x86: die if they compile this in! */

#define cpuid_has_feature(feature)     BUILD_ASSERT_OR_ZERO(0)
#define cpuid(info, buf)               BUILD_ASSERT_OR_ZERO(0)
...

Cheers,
Rusty.


More information about the ccan mailing list