diff --git a/README.md b/README.md index 3a95ae2..0e12ac6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ # e2k-libs -Small e2k single header libraries for everyone to use \ No newline at end of file +Small e2k single header libraries for everyone to use + +## cpuid + +A CPUID that doesn't rely on Linux kernel to get processor info. + +Maybe useful for those cases, when /proc/cpuinfo is not available or invalid. diff --git a/cpuid/e2k_cpuid.h b/cpuid/e2k_cpuid.h new file mode 100644 index 0000000..1e88517 --- /dev/null +++ b/cpuid/e2k_cpuid.h @@ -0,0 +1,200 @@ +/* +Copyright (c) 2020 Alibek Omarov + +This work is free. You can redistribute it and/or modify it under the +terms of the Do What The Fuck You Want To Public License, Version 2, +as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. +*/ +#pragma once +#ifndef E2K_CPUID_H +#define E2K_CPUID_H + +#include +#include + +/** + * e2k cpuid single-header library, v1 + * + * Changes from v0: + * - initial release + * + **/ + +typedef struct e2k_cpuid_s +{ + char model[16]; // string representation of model + uint32_t mdl; // actual model id + uint32_t revision; + uint32_t l2_wb_len; // L2 cache writeback length + uint32_t cores; + uint32_t pn; // ??? + uint32_t vt_version; + uint32_t have_vt; + uint32_t is_guest; +} e2k_cpuid_t; + +typedef enum e2k_cpuid_mdl_e +{ + MDL_E2S = 0x03, /* Elbrus-4C */ + MDL_ES2 = 0x04, /* Elbrus-2C+ */ + MDL_ES2_NO_DSP = 0x06, /* Elbrus-2CM */ + MDL_E8C = 0x07, /* Elbrus-8C */ + MDL_E1CP = 0x08, /* Elbrus-1C+ */ + MDL_E8C2 = 0x09, /* Elbrus-8C2 */ + MDL_E12C = 0x0a, /* Elbrus-12C */ + MDL_E16C = 0x0b, /* Elbrus-16C */ + MDL_E2C3 = 0x0c, /* Elbrus-2C3 */ +} e2k_cpuid_mdl_t; + +int e2k_cpuid( e2k_cpuid_t *cpuid ); +int e2k_cpuid_from_regs( e2k_cpuid_t *cpuid, uint64_t idr, uint32_t core_mode /* only v6 */ ); +int e2k_cpuid_model_from_mdl( char *out, size_t len, uint32_t mdl ); + +#ifdef E2K_CPUID_IMPLEMENTATION + +#include + +#define extract32( v, s, len ) ((( v ) >> ( s )) & ( ~0U >> ( 32 - ( len )))) +#define extract64( v, s, len ) ((( v ) >> ( s )) & ( ~0ULL >> ( 64 - ( len )))) + +#define IDR_MDL_START 0 +#define IDR_MDL_BITS 8 +#define IDR_REV_START ( IDR_MDL_START + IDR_MDL_BITS ) +#define IDR_REV_BITS 4 +#define IDR_WBL_START ( IDR_REV_START + IDR_REV_BITS ) +#define IDR_WBL_BITS 3 +#define IDR_CORE_START ( IDR_WBL_START + IDR_WBL_BITS ) +#define IDR_CORE_BITS 5 +#define IDR_PN_START ( IDR_CORE_START + IDR_CORE_BITS ) +#define IDR_PN_BITS 4 +#define IDR_HW_VT_START ( IDR_PN_START + IDR_PN_BITS ) +#define IDR_HW_VT_BITS 1 +#define IDR_HW_VT_VER_START ( IDR_HW_VT_START + IDR_HW_VT_BITS ) +#define IDR_HW_VT_VER_BITS 4 + +#define CORE_MODE_GUEST_START 3 +#define CORE_MODE_GUEST_BITS 1 + +#define IDR_WBL_TO_BYTES(wbl) ((wbl) ? (1 << (wbl + 4)) : 1) + +int e2k_cpuid_model_from_mdl( char *out, size_t len, uint32_t mdl ) +{ + int retval = 0; + const char *str; + + switch( mdl ) + { + case MDL_E2S: str = "Elbrus-4C"; break; + case MDL_ES2: str = "Elbrus-2C+"; break; + case MDL_ES2_NO_DSP: str = "Elbrus-2CM"; break; + case MDL_E8C: str = "Elbrus-8C"; break; + case MDL_E1CP: str = "Elbrus-1C+"; break; + case MDL_E8C2: str = "Elbrus-8C2"; break; + case MDL_E12C: str = "Elbrus-12C"; break; + case MDL_E16C: str = "Elbrus-16C"; break; + case MDL_E2C3: str = "Elbrus-2C3"; break; + default: + { + if( mdl < MDL_E2S ) + str = "E3M LOL"; + else if( mdl > MDL_E2C3 ) + str = "UpdateCpuidPls"; + retval = -1; + } + } + + strncpy( out, str, len ); + out[len - 1] = 0; + + return retval; +} + +int e2k_cpuid_from_regs( e2k_cpuid_t *cpuid, uint64_t idr, uint32_t core_mode ) +{ + int retval = 0; + + memset( cpuid, 0, sizeof( *cpuid )); + + cpuid->mdl = extract64( idr, IDR_MDL_START, IDR_MDL_BITS ); + cpuid->revision = extract64( idr, IDR_REV_START, IDR_REV_BITS ); + cpuid->l2_wb_len = extract64( idr, IDR_WBL_START, IDR_WBL_BITS ); + + // only v6 and higher + if( cpuid->mdl > MDL_E12C ) + { + cpuid->cores = extract64( idr, IDR_CORE_START, IDR_CORE_BITS ); + cpuid->pn = extract64( idr, IDR_PN_START, IDR_PN_BITS ); + cpuid->have_vt = extract64( idr, IDR_HW_VT_START, IDR_HW_VT_BITS ); + cpuid->vt_version = extract64( idr, IDR_HW_VT_VER_START, IDR_HW_VT_VER_BITS ); + + cpuid->is_guest = extract32( core_mode, CORE_MODE_GUEST_START, CORE_MODE_GUEST_BITS ); + } + + // finalize data + cpuid->l2_wb_len = IDR_WBL_TO_BYTES( cpuid->l2_wb_len ); + retval = e2k_cpuid_model_from_mdl( cpuid->model, sizeof( cpuid->model ), cpuid->mdl ); + + return retval; +} + +uint32_t e2k_get_core_mode( void ) __attribute__((noinline)); +#if __iset__ >= 6 +uint32_t e2k_get_core_mode( void ) +{ + register uint32_t core_mode = 0; + #pragma no_asm_inline + __asm__ volatile + ( + "\t rrs \t%%core_mode, %0\n" + : "=r"(core_mode) + ); +} +#else +__asm__ volatile +( +"e2k_get_core_mode:\n" +".dword 0x04108022\n" +".dword 0x58ecc082\n" +".dword 0x040000c4\n" +".dword 0x01c00000\n" +".dword 0x00000000\n" +".dword 0x00000110\n" +".dword 0x04000001\n" +".dword 0x90c0c083\n" +".dword 0x04100011\n" +".dword 0x3e04c083\n" +".dword 0x01c00000\n" +".dword 0x00000000\n" +".dword 0x04004292\n" +".dword 0x8cc68380\n" +".dword 0xf0000000\n" +".dword 0x00000000\n" +".dword 0x00001001\n" +".dword 0xc0000c20\n" +); +#endif + +int e2k_cpuid( e2k_cpuid_t *cpuid ) +{ + uint64_t idr; + uint32_t core_mode = 0; + uint32_t mdl; + + __asm__ volatile( "\trrd \t%%idr, %0\n" : "=r"(idr)); + + mdl = extract64( idr, IDR_MDL_START, IDR_MDL_BITS ); + + if( mdl >= MDL_E12C ) + { + core_mode = e2k_get_core_mode(); + } + + return e2k_cpuid_from_regs( cpuid, idr, core_mode ); +} + +#undef IDR_WBL_TO_BYTES +#undef extract32 +#undef extract64 + +#endif // E2K_CPUID_IMPLEMENTATION +#endif // E2K_CPUID_H diff --git a/cpuid/example.c b/cpuid/example.c new file mode 100644 index 0000000..081cbf4 --- /dev/null +++ b/cpuid/example.c @@ -0,0 +1,19 @@ +#define E2K_CPUID_IMPLEMENTATION +#include "e2k_cpuid.h" +#include + +int main( int argc, char **argv ) +{ + int err; + e2k_cpuid_t cpuid; + if(( err = e2k_cpuid( &cpuid ))) + { + printf( "Error %d\n", err ); + return err; + } + + printf( "Model: %s\nRevision: %d\nCores: %d\nL2 writeback length: %d\nHave VT: %d\nVT version: %d\nUnder KVM: %d\n", + cpuid.model, cpuid.revision, cpuid.cores, cpuid.l2_wb_len, cpuid.have_vt, cpuid.vt_version, cpuid.is_guest ); + + return 0; +}