packages feed

crypton-2.2.0: Crypto/System/CPU.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE PatternSynonyms #-}

-- |
-- Module      : Crypto.System.CPU
-- License     : BSD-style
-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>
-- Stability   : experimental
-- Portability : unknown
--
-- Gives information about crypton runtime environment.
module Crypto.System.CPU (
    -- The names are bundled with the type rather than listed as
    -- `pattern' exports, so an importer writes ProcessorOption (..), or
    -- names the ones it wants, as it would for a type with constructors.
    ProcessorOption (
        -- x86
        AESNI,
        PCLMUL,
        SSSE3,
        AVX,
        AVX2,
        SHANI,
        MOVBE,
        ADX,
        VAES,
        VAES512,
        -- AArch64
        NEON,
        ARMAES,
        ARMPMULL,
        ARMSHA1,
        ARMSHA2,
        ARMSHA512,
        -- PowerISA
        PPCAES,
        PPCVPMSUM
    ),
    processorOptions,

    -- * Questions that do not name an architecture
    hasAESAcceleration,
    hasGHASHAcceleration,
) where

import Control.Monad (filterM)
import Data.List (sort)
import Data.Word (Word16)
import Foreign.C.Types (CInt (..), CUInt (..))
import Crypto.Internal.Compat

-- | A processor feature crypton looked for, and dispatches on where it
-- finds it.
--
-- This is a number with names rather than a sum of constructors, and the
-- names are pattern synonyms with no @COMPLETE@ pragma, so a @case@ over
-- them needs a catch-all and a feature named in a later release breaks
-- nothing that compiled against this one.  The same reason 'Show' is
-- written out below: a program built against an older crypton still says
-- something useful about a value from a newer one.
--
-- The names are the processor's, not the operation's.  'AESNI' is x86's
-- and 'ARMAES' is AArch64's, and a machine reports only the ones it has;
-- ask 'hasAESAcceleration' if the question is whether AES is fast here.
--
-- They are bundled with the type in the export list, so @ProcessorOption
-- (..)@ brings in all of them and naming one brings in that one, as for a
-- type with constructors.  The constructor underneath is not exported:
-- these values say what the processor was found to have, and a caller has
-- nothing to build.
newtype ProcessorOption = ProcessorOption Word16
    deriving (Eq, Ord)

-- | Support for AES instructions, with flag @support_aesni@.
pattern AESNI :: ProcessorOption
pattern AESNI = ProcessorOption 0

-- | Support for CLMUL instructions, with flag @support_pclmuldq@.
pattern PCLMUL :: ProcessorOption
pattern PCLMUL = ProcessorOption 1

-- | Supplemental SSE3.
pattern SSSE3 :: ProcessorOption
pattern SSSE3 = ProcessorOption 3

-- | AVX, and an operating system that saves its registers.
pattern AVX :: ProcessorOption
pattern AVX = ProcessorOption 4

-- | AVX2, and an operating system that saves its registers.
pattern AVX2 :: ProcessorOption
pattern AVX2 = ProcessorOption 5

-- | The SHA extensions, @sha1rnds4@ and @sha256rnds2@ and their neighbours.
pattern SHANI :: ProcessorOption
pattern SHANI = ProcessorOption 6

-- | The byte-swapping load.
pattern MOVBE :: ProcessorOption
pattern MOVBE = ProcessorOption 7

-- | @MULX@, @ADCX@ and @ADOX@: the two independent carry chains.
pattern ADX :: ProcessorOption
pattern ADX = ProcessorOption 8

-- | The AES and carry-less multiply instructions in their 256-bit form.
pattern VAES :: ProcessorOption
pattern VAES = ProcessorOption 9

-- | The same pair in their 512-bit form.
pattern VAES512 :: ProcessorOption
pattern VAES512 = ProcessorOption 10

-- | Advanced SIMD, which is not optional on AArch64.
pattern NEON :: ProcessorOption
pattern NEON = ProcessorOption 11

-- | The ARMv8 AES instructions.
pattern ARMAES :: ProcessorOption
pattern ARMAES = ProcessorOption 12

-- | @PMULL@, the ARMv8 carry-less multiply.
pattern ARMPMULL :: ProcessorOption
pattern ARMPMULL = ProcessorOption 13

-- | The ARMv8 SHA-1 instructions.
pattern ARMSHA1 :: ProcessorOption
pattern ARMSHA1 = ProcessorOption 14

-- | The ARMv8 SHA-256 instructions.
pattern ARMSHA2 :: ProcessorOption
pattern ARMSHA2 = ProcessorOption 15

-- | The ARMv8.2 SHA-512 instructions, which are optional where SHA-256's
-- are not.
pattern ARMSHA512 :: ProcessorOption
pattern ARMSHA512 = ProcessorOption 16

-- | Support for the PowerISA 2.07 vector AES instructions, which POWER8 was
-- the first to implement.
pattern PPCAES :: ProcessorOption
pattern PPCAES = ProcessorOption 17

-- | Support for @vpmsumd@, the vector carry-less multiply that came with
-- them, which is what makes GHASH fast.
pattern PPCVPMSUM :: ProcessorOption
pattern PPCVPMSUM = ProcessorOption 18

-- | Named where the name is known, numbered where it is not, so that a
-- binary built against an older crypton can still print a value a newer one
-- produced.
instance Show ProcessorOption where
    show AESNI = "AESNI"
    show PCLMUL = "PCLMUL"
    show SSSE3 = "SSSE3"
    show AVX = "AVX"
    show AVX2 = "AVX2"
    show SHANI = "SHANI"
    show MOVBE = "MOVBE"
    show ADX = "ADX"
    show VAES = "VAES"
    show VAES512 = "VAES512"
    show NEON = "NEON"
    show ARMAES = "ARMAES"
    show ARMPMULL = "ARMPMULL"
    show ARMSHA1 = "ARMSHA1"
    show ARMSHA2 = "ARMSHA2"
    show ARMSHA512 = "ARMSHA512"
    show PPCAES = "PPCAES"
    show PPCVPMSUM = "PPCVPMSUM"
    show (ProcessorOption n) = "ProcessorOption " ++ show n

-- | Options which have been enabled at compile time and are supported by the
-- current CPU.
--
-- Sorted, and without repeats.  A machine reports the names of its own
-- architecture only: an AArch64 processor with AES says 'ARMAES', not
-- 'AESNI', which it does not have.
processorOptions :: [ProcessorOption]
processorOptions = unsafeDoIO (sort <$> filterM askC allOptions)
  where
    -- 2 is not asked for and has no name: it was RDRAND, which crypton no
    -- longer dispatches on.  The number is left out rather than reused, so
    -- that the others keep the values they had.
    allOptions = [ProcessorOption n | n <- [0 .. 18], n /= 2]
    askC (ProcessorOption n) =
        (/= 0) <$> crypton_cpu_option (fromIntegral n)
{-# NOINLINE processorOptions #-}

-- | Is there hardware AES on this machine?
--
-- The instructions have different names on different architectures, and a
-- caller that wants to know whether AES-GCM will be fast wants this rather
-- than either name.
hasAESAcceleration :: Bool
hasAESAcceleration =
    any
        (`elem` processorOptions)
        [AESNI, ARMAES, PPCAES]

-- | Is there a hardware carry-less multiply, which is what GHASH, and so
-- AES-GCM, spends its time in once AES itself is fast?
hasGHASHAcceleration :: Bool
hasGHASHAcceleration =
    any
        (`elem` processorOptions)
        [PCLMUL, ARMPMULL, PPCVPMSUM]

foreign import ccall unsafe "crypton_cpu_option"
    crypton_cpu_option :: CUInt -> IO CInt