diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -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.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/basic-cpuid.cabal b/basic-cpuid.cabal
new file mode 100644
--- /dev/null
+++ b/basic-cpuid.cabal
@@ -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
+
diff --git a/cbits/cpuid.c b/cbits/cpuid.c
new file mode 100644
--- /dev/null
+++ b/cbits/cpuid.c
@@ -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
+}
diff --git a/cbits/cpuid.h b/cbits/cpuid.h
new file mode 100644
--- /dev/null
+++ b/cbits/cpuid.h
@@ -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
diff --git a/src/System/Cpuid/Basic.hs b/src/System/Cpuid/Basic.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Cpuid/Basic.hs
@@ -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
