packages feed

basic-cpuid (empty) → 0.1.0.0

raw patch · 7 files changed

+319/−0 lines, 7 filesdep +basesetup-changed

Dependencies added: base

Files

+ ChangeLog.md view
@@ -0,0 +1,6 @@+# Revision history for basic-cpuid++## 0.1.0.0  -- 2017-06-27++* First version. Provides basic access to the CPUID and XGETBV instructions as+  well as SSE2/AVX2/AVX512f detection functions.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Anselm Jonas Scholl++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Anselm Jonas Scholl nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ basic-cpuid.cabal view
@@ -0,0 +1,37 @@+name:                basic-cpuid+version:             0.1.0.0+synopsis:            A small package to access the cpuid instruction directly.+description:         Similar to the cpuid package this package allows one to+                     call the CPUID instruction from plain Haskell. In contrast+                     to the cpuid package it is very minimalistic and provides+                     no additional helper functions. On the other side it is+                     possible to build this package on every platform. If the+                     platform does not support the CPUID instruction, calling+                     it from Haskell will just throw an exception.+license:             BSD3+license-file:        LICENSE+author:              Anselm Jonas Scholl+maintainer:          anselm.scholl@tu-harburg.de+copyright:           (c) 2017 Anselm Jonas Scholl+category:            System+build-type:          Simple+extra-source-files:  ChangeLog.md+cabal-version:       >=1.10++library+  exposed-modules:     System.Cpuid.Basic+  other-extensions:    ForeignFunctionInterface, CPP+  if arch(i386) || arch(x86_64)+    cpp-options:       -DCPUID_SUPPORT+  c-sources:           cbits/cpuid.c+  include-dirs:        cbits+  install-includes:    cpuid.h+  build-depends:       base >=4.7 && < 5+  hs-source-dirs:      src+  ghc-options:         -Wall+  default-language:    Haskell2010++source-repository head+  type:     git+  location: git://github.com/ajscholl/basic-cpuid.git+
+ cbits/cpuid.c view
@@ -0,0 +1,39 @@++#include "cpuid.h"++void call_cpuid(uint32_t eax, uint32_t ecx, uint32_t *result) {+#if defined(__x86_64__) || defined(__i386__)+    __asm__ __volatile__(+		    "cpuid\r\n"+		    "mov %%eax, %0\r\n"+		    "mov %%ebx, %1\r\n"+		    "mov %%ecx, %2\r\n"+		    "mov %%edx, %3\r\n"+		    : "=g" (result[0]), "=g" (result[1]), "=g" (result[2]), "=g" (result[3])+		    : "a" (eax), "c" (ecx)+		    : "memory", "ebx", "edx", "cc"+		);+#else+    result[0] = 0;+    result[1] = 0;+    result[2] = 0;+    result[3] = 0;+#endif+}++uint64_t call_xgetbv(uint32_t ecx) {+#if defined(__x86_64__) || defined(__i386__)+    uint64_t rax;+    __asm__ __volatile__(+            "xgetbv\r\n"+            "shl $32, %%rdx \r\n"+            "or %%rdx, %%rax \r\n"+            : "=a" (rax)+            : "c" (ecx)+            : "rdx", "cc"+        );+    return rax;+#else+    return 0;+#endif+}
+ cbits/cpuid.h view
@@ -0,0 +1,9 @@+#ifndef CPUID_H+#define CPUID_H++#include <stdint.h>++void call_cpuid(uint32_t eax, uint32_t ecx, uint32_t *result);+uint64_t call_xgetbv(uint32_t ecx);++#endif
+ src/System/Cpuid/Basic.hs view
@@ -0,0 +1,196 @@+{-|+Module      : System.Cpuid.Basic+Description : Call the CPUID instruction from Haskell.+Copyright   : (c) 2017, Anselm Jonas Scholl+License     : BSD3+Maintainer  : anselm.scholl@tu-harburg.de+Stability   : experimental+Portability : x86++This module gives access to the CPUID instruction on x86 based systems.+The module will still build on other systems but will not provide any useful+functionality instead.+-}+{-# LANGUAGE CPP                      #-}+#ifdef CPUID_SUPPORT+{-# LANGUAGE ForeignFunctionInterface #-}+#endif+{-# LANGUAGE NondecreasingIndentation #-}+module System.Cpuid.Basic (+     -- * CPUID support+     CpuidArg(..)+    ,CpuidResult(..)+    ,cpuidSupported+    ,cpuid+    ,cpuidMaybe+    ,cpuidHighestFunctionParameter++    -- * XGETBV support+    ,XGetBVArg+    ,XGetBVResult(..)+    ,xgetbvSupport+    ,xgetbv+    ,xgetbvMaybe++    -- * Utilities+    ,supportsSSE2+    ,supportsAVX2+    ,supportsAVX512f+    ) where++#ifdef CPUID_SUPPORT+import Control.Applicative+import Prelude+import Foreign+#else+import Data.Word+#endif++-- * CPUID support++-- | Argument for the CPUID instruction.+data CpuidArg = CpuidArg {+     caEAX :: !Word32 -- ^ Value to query for.+    ,caECX :: !Word32 -- ^ Additional value for queries. You can leave this at 0 most of the time.+} deriving (Show, Eq)++-- | Result of a call to the CPUID instruction.+data CpuidResult = CpuidResult {+     crEAX :: !Word32 -- ^ Value of EAX after the CPUID instruction.+    ,crEBX :: !Word32 -- ^ Value of EBX after the CPUID instruction.+    ,crECX :: !Word32 -- ^ Value of ECX after the CPUID instruction.+    ,crEDX :: !Word32 -- ^ Value of EDX after the CPUID instruction.+} deriving (Show, Eq)++-- | True if the platform supports the CPUID instruction (that is, if this code runs on x86 or AMD64).+cpuidSupported :: Bool+#ifdef CPUID_SUPPORT+cpuidSupported = True+#else+cpuidSupported = False+#endif++-- | Call the CPUID instruction. If the instruction is not supported, throw an error.+cpuid :: CpuidArg -> IO CpuidResult+#ifdef CPUID_SUPPORT+cpuid (CpuidArg eax ecx) = allocaBytes 16 $ \ ptr -> do+    call_cpuid eax ecx ptr+    CpuidResult <$>+        peekElemOff ptr 0 <*>+        peekElemOff ptr 1 <*>+        peekElemOff ptr 2 <*>+        peekElemOff ptr 3++foreign import ccall unsafe "call_cpuid" call_cpuid :: Word32 -> Word32 -> Ptr Word32 -> IO ()+#else+cpuid _ = fail "CPUID instruction unsupported on this platform."+#endif++-- | A safe version of 'cpuid' which never throws exceptions if a 'Just' is returned.+cpuidMaybe :: Maybe (CpuidArg -> IO CpuidResult)+cpuidMaybe = if cpuidSupported+    then Just cpuid+    else Nothing++-- | Get the highest supported function parameter (EAX) for the CPUID instruction.+--   Returns 0 if CPUID is unsupported.+cpuidHighestFunctionParameter :: IO Word32+cpuidHighestFunctionParameter = if cpuidSupported+    then crEAX <$> cpuid (CpuidArg 0 0)+    else pure 0++-- * XGETBV support++-- | Argument for the XGETBV instruction.+type XGetBVArg = Word32++-- | Result of a call to the XGETBV instruction.+data XGetBVResult = XGetBVResult {+     xgEAX :: !Word32+    ,xgEDX :: !Word32+} deriving (Show, Eq)++-- | Returns true if the platform supports the XGETBV instruction (that is, if the+--   code runs on x86 or AMD64) AND the processor (or operating system) support it+--   (that is, the OSXSAVE bit is set)+xgetbvSupport :: IO Bool+#ifdef CPUID_SUPPORT+xgetbvSupport = do+    ecx <- crECX <$> cpuid (CpuidArg 1 0)+    -- check bit 26 and 27 (XGETBV support and OSXSAVE support enabled by OS)+    pure $ (ecx .&. 0xC000000) == 0xC000000+#else+xgetbvSupport = pure False+#endif++-- | Call the XGETBV instruction. If the instruction is not supported, throw an error.+--   You also have to check for support of the instruction by the operating system+--   and the processor first (use 'xgetbvSupport').+xgetbv :: XGetBVArg -> IO XGetBVResult+#ifdef CPUID_SUPPORT+xgetbv ecx = do+    edx_eax <- call_xgetbv ecx+    pure XGetBVResult {+         xgEAX = fromIntegral $ edx_eax .&. 0xFFFFFFFF+        ,xgEDX = fromIntegral $ edx_eax `shiftR` 32+    }++foreign import ccall unsafe "call_xgetbv" call_xgetbv :: Word32 -> IO Word64+#else+xgetbv _ = fail "XGETBV instruction unsupported on this platform."+#endif++-- | A safe version of 'xgetbv' which never throws exceptions if a 'Just' is returned.+xgetbvMaybe :: IO (Maybe (XGetBVArg -> IO XGetBVResult))+xgetbvMaybe = do+    support <- xgetbvSupport+    if support+    then pure $ Just xgetbv+    else pure Nothing++-- * Utilities++-- | Check whether the processor indicated support for SSE2. Note that this does+--   not mean the OS will save the XMM upon context switches.+supportsSSE2 :: IO Bool+supportsSSE2 = do+    -- check if we can call the right CPUID instructions+    -- this will return 0 if CPUID is unsupported+    highestFunParam <- cpuidHighestFunctionParameter+    if highestFunParam < 1 then pure False else do+    -- check SSE2 support+    edx <- crEDX <$> cpuid (CpuidArg 1 0)+    pure $ (edx .&. 0x4000000) == 0x4000000++-- | Check whether OS and CPU support the AVX2 instruction set.+supportsAVX2 :: IO Bool+supportsAVX2 = do+    -- check if we can call the right CPUID instructions+    -- this will return 0 if CPUID is unsupported+    highestFunParam <- cpuidHighestFunctionParameter+    if highestFunParam < 7 then pure False else do+    -- check AVX support+    ecx <- crECX <$> cpuid (CpuidArg 1 0)+    if ((ecx .&. 0x10000000) /= 0x10000000) then pure False else do+    -- check AVX2 support+    ebx <- crEBX <$> cpuid (CpuidArg 7 0)+    if ((ebx .&. 0x20) /= 0x20) then pure False else do+    xSupport <- xgetbvSupport+    if not xSupport then pure False else do+    -- specify 0 for XFEATURE_ENABLED_MASK register+    eax <- xgEAX <$> xgetbv 0+    -- check OS has enabled both XMM and YMM state support+    pure $ eax .&. 0x06 == 0x06++-- | Check whether OS and CPU support the AVX512f instruction set.+supportsAVX512f :: IO Bool+supportsAVX512f = do+    avx2 <- supportsAVX2+    if not avx2 then pure False else do+    -- check AVX512f support+    ebx <- crEBX <$> cpuid (CpuidArg 7 0)+    if ((ebx .&. 0x10000) /= 0x10000) then pure False else do+    -- specify 0 for XFEATURE_ENABLED_MASK register+    eax <- xgEAX <$> xgetbv 0+    -- check OS has enabled full ZMM state support+    pure $ eax .&. 0xE0 == 0xE0