mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-04 03:21:42 +00:00
@@ -0,0 +1,107 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
|
||||
#if defined(__cpp_lib_constexpr_bitset) && __cpp_lib_constexpr_bitset >= 202207L
|
||||
# define OAKNUT_CPU_FEATURES_CONSTEXPR constexpr
|
||||
#else
|
||||
# define OAKNUT_CPU_FEATURES_CONSTEXPR
|
||||
#endif
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
// NOTE: This file contains code that can be compiled on non-arm64 systems.
|
||||
// For run-time CPU feature detection, include feature_detection.hpp
|
||||
|
||||
enum class CpuFeature {
|
||||
#define OAKNUT_CPU_FEATURE(name) name,
|
||||
#include "oaknut/impl/cpu_feature.inc.hpp"
|
||||
#undef OAKNUT_CPU_FEATURE
|
||||
};
|
||||
|
||||
constexpr std::size_t cpu_feature_count = 0
|
||||
#define OAKNUT_CPU_FEATURE(name) +1
|
||||
#include "oaknut/impl/cpu_feature.inc.hpp"
|
||||
#undef OAKNUT_CPU_FEATURE
|
||||
;
|
||||
|
||||
class CpuFeatures final {
|
||||
public:
|
||||
constexpr CpuFeatures() = default;
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR explicit CpuFeatures(std::initializer_list<CpuFeature> features)
|
||||
{
|
||||
for (CpuFeature f : features) {
|
||||
m_bitset.set(static_cast<std::size_t>(f));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool has(CpuFeature feature) const
|
||||
{
|
||||
if (static_cast<std::size_t>(feature) >= cpu_feature_count)
|
||||
return false;
|
||||
return m_bitset[static_cast<std::size_t>(feature)];
|
||||
}
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures& operator&=(const CpuFeatures& other) noexcept
|
||||
{
|
||||
m_bitset &= other.m_bitset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures& operator|=(const CpuFeatures& other) noexcept
|
||||
{
|
||||
m_bitset |= other.m_bitset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures& operator^=(const CpuFeatures& other) noexcept
|
||||
{
|
||||
m_bitset ^= other.m_bitset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator~() const noexcept
|
||||
{
|
||||
CpuFeatures result;
|
||||
result.m_bitset = ~m_bitset;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
using bitset = std::bitset<cpu_feature_count>;
|
||||
|
||||
friend OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator&(const CpuFeatures& a, const CpuFeatures& b) noexcept;
|
||||
friend OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator|(const CpuFeatures& a, const CpuFeatures& b) noexcept;
|
||||
friend OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator^(const CpuFeatures& a, const CpuFeatures& b) noexcept;
|
||||
|
||||
bitset m_bitset;
|
||||
};
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator&(const CpuFeatures& a, const CpuFeatures& b) noexcept
|
||||
{
|
||||
CpuFeatures result;
|
||||
result.m_bitset = a.m_bitset & b.m_bitset;
|
||||
return result;
|
||||
}
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator|(const CpuFeatures& a, const CpuFeatures& b) noexcept
|
||||
{
|
||||
CpuFeatures result;
|
||||
result.m_bitset = a.m_bitset | b.m_bitset;
|
||||
return result;
|
||||
}
|
||||
|
||||
OAKNUT_CPU_FEATURES_CONSTEXPR CpuFeatures operator^(const CpuFeatures& a, const CpuFeatures& b) noexcept
|
||||
{
|
||||
CpuFeatures result;
|
||||
result.m_bitset = a.m_bitset ^ b.m_bitset;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 1
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 0
|
||||
# include "oaknut/feature_detection/feature_detection_apple.hpp"
|
||||
#elif defined(__FreeBSD__)
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 1
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 1
|
||||
# include "oaknut/feature_detection/feature_detection_freebsd.hpp"
|
||||
#elif defined(__linux__)
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 1
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 1
|
||||
# include "oaknut/feature_detection/feature_detection_linux.hpp"
|
||||
#elif defined(__NetBSD__)
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 1
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 2
|
||||
# include "oaknut/feature_detection/feature_detection_netbsd.hpp"
|
||||
#elif defined(__OpenBSD__)
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 1
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 1
|
||||
# include "oaknut/feature_detection/feature_detection_openbsd.hpp"
|
||||
#elif defined(_WIN32)
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 1
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 2
|
||||
# include "oaknut/feature_detection/feature_detection_w32.hpp"
|
||||
#else
|
||||
# define OAKNUT_CPU_FEATURE_DETECTION 0
|
||||
# define OAKNUT_SUPPORTS_READING_ID_REGISTERS 0
|
||||
# warning "Unsupported operating system for CPU feature detection"
|
||||
# include "oaknut/feature_detection/feature_detection_generic.hpp"
|
||||
#endif
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
// Ref: https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline bool detect_feature(const char* const sysctl_name)
|
||||
{
|
||||
int result = 0;
|
||||
std::size_t result_size = sizeof(result);
|
||||
if (::sysctlbyname(sysctl_name, &result, &result_size, nullptr, 0) == 0) {
|
||||
return result != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline CpuFeatures detect_features_via_sysctlbyname()
|
||||
{
|
||||
CpuFeatures result;
|
||||
|
||||
if (detail::detect_feature("hw.optional.AdvSIMD") || detail::detect_feature("hw.optional.neon"))
|
||||
result |= CpuFeatures{CpuFeature::ASIMD};
|
||||
if (detail::detect_feature("hw.optional.floatingpoint"))
|
||||
result |= CpuFeatures{CpuFeature::FP};
|
||||
if (detail::detect_feature("hw.optional.AdvSIMD_HPFPCvt") || detail::detect_feature("hw.optional.neon_hpfp"))
|
||||
result |= CpuFeatures{CpuFeature::FP16Conv};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_BF16"))
|
||||
result |= CpuFeatures{CpuFeature::BF16};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_DotProd"))
|
||||
result |= CpuFeatures{CpuFeature::DotProd};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_FCMA") || detail::detect_feature("hw.optional.armv8_3_compnum"))
|
||||
result |= CpuFeatures{CpuFeature::FCMA};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_FHM") || detail::detect_feature("hw.optional.armv8_2_fhm"))
|
||||
result |= CpuFeatures{CpuFeature::FHM};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_FP16") || detail::detect_feature("hw.optional.neon_fp16"))
|
||||
result |= CpuFeatures{CpuFeature::FP16};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_FRINTTS"))
|
||||
result |= CpuFeatures{CpuFeature::FRINTTS};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_I8MM"))
|
||||
result |= CpuFeatures{CpuFeature::I8MM};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_JSCVT"))
|
||||
result |= CpuFeatures{CpuFeature::JSCVT};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_RDM"))
|
||||
result |= CpuFeatures{CpuFeature::RDM};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_FlagM"))
|
||||
result |= CpuFeatures{CpuFeature::FlagM};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_FlagM2"))
|
||||
result |= CpuFeatures{CpuFeature::FlagM2};
|
||||
if (detail::detect_feature("hw.optional.armv8_crc32"))
|
||||
result |= CpuFeatures{CpuFeature::CRC32};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_LRCPC"))
|
||||
result |= CpuFeatures{CpuFeature::LRCPC};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_LRCPC2"))
|
||||
result |= CpuFeatures{CpuFeature::LRCPC2};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_LSE") || detail::detect_feature("hw.optional.armv8_1_atomics"))
|
||||
result |= CpuFeatures{CpuFeature::LSE};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_LSE2"))
|
||||
result |= CpuFeatures{CpuFeature::LSE2};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_AES"))
|
||||
result |= CpuFeatures{CpuFeature::AES};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_PMULL"))
|
||||
result |= CpuFeatures{CpuFeature::PMULL};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_SHA1"))
|
||||
result |= CpuFeatures{CpuFeature::SHA1};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_SHA256"))
|
||||
result |= CpuFeatures{CpuFeature::SHA256};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_SHA512") || detail::detect_feature("hw.optional.armv8_2_sha512"))
|
||||
result |= CpuFeatures{CpuFeature::SHA512};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_SHA3") || detail::detect_feature("hw.optional.armv8_2_sha3"))
|
||||
result |= CpuFeatures{CpuFeature::SHA3};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_BTI"))
|
||||
result |= CpuFeatures{CpuFeature::BTI};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_DPB"))
|
||||
result |= CpuFeatures{CpuFeature::DPB};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_DPB2"))
|
||||
result |= CpuFeatures{CpuFeature::DPB2};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_ECV"))
|
||||
result |= CpuFeatures{CpuFeature::ECV};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_SB"))
|
||||
result |= CpuFeatures{CpuFeature::SB};
|
||||
if (detail::detect_feature("hw.optional.arm.FEAT_SSBS"))
|
||||
result |= CpuFeatures{CpuFeature::SSBS};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
return detect_features_via_sysctlbyname();
|
||||
}
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/feature_detection_hwcaps.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
#include "oaknut/feature_detection/read_id_registers_directly.hpp"
|
||||
|
||||
#ifndef AT_HWCAP
|
||||
# define AT_HWCAP 16
|
||||
#endif
|
||||
#ifndef AT_HWCAP2
|
||||
# define AT_HWCAP2 26
|
||||
#endif
|
||||
|
||||
#if __FreeBSD_version < 1300114
|
||||
# error "Incompatible ABI change (incorrect HWCAP definitions on earlier FreeBSD versions)"
|
||||
#endif
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline unsigned long getauxval(int aux)
|
||||
{
|
||||
unsigned long result = 0;
|
||||
if (::elf_aux_info(aux, &result, static_cast<int>(sizeof result)) == 0) {
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline CpuFeatures detect_features_via_hwcap()
|
||||
{
|
||||
const unsigned long hwcap = detail::getauxval(AT_HWCAP);
|
||||
const unsigned long hwcap2 = detail::getauxval(AT_HWCAP2);
|
||||
return detect_features_via_hwcap(hwcap, hwcap2);
|
||||
}
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers()
|
||||
{
|
||||
// HWCAP_CPUID is falsely not set on many FreeBSD kernel versions,
|
||||
// so we don't bother checking it.
|
||||
return id::read_id_registers_directly();
|
||||
}
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
return detect_features_via_hwcap();
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
return CpuFeatures{CpuFeature::FP, CpuFeature::ASIMD};
|
||||
}
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<std::size_t... bits>
|
||||
constexpr bool bit_test(unsigned long value)
|
||||
{
|
||||
return (((value >> bits) & 1) && ...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline CpuFeatures detect_features_via_hwcap(unsigned long hwcap, unsigned long hwcap2)
|
||||
{
|
||||
CpuFeatures result;
|
||||
|
||||
#define OAKNUT_DETECT_CAP(FEAT, ...) \
|
||||
if (detail::bit_test<__VA_ARGS__>(hwcap)) { \
|
||||
result |= CpuFeatures{CpuFeature::FEAT}; \
|
||||
}
|
||||
#define OAKNUT_DETECT_CAP2(FEAT, ...) \
|
||||
if (detail::bit_test<__VA_ARGS__>(hwcap2)) { \
|
||||
result |= CpuFeatures{CpuFeature::FEAT}; \
|
||||
}
|
||||
|
||||
OAKNUT_DETECT_CAP(FP, 0) // HWCAP_FP
|
||||
OAKNUT_DETECT_CAP(ASIMD, 1) // HWCAP_ASIMD
|
||||
// HWCAP_EVTSTRM (2)
|
||||
OAKNUT_DETECT_CAP(AES, 3) // HWCAP_AES
|
||||
OAKNUT_DETECT_CAP(PMULL, 4) // HWCAP_PMULL
|
||||
OAKNUT_DETECT_CAP(SHA1, 5) // HWCAP_SHA1
|
||||
OAKNUT_DETECT_CAP(SHA256, 6) // HWCAP_SHA2
|
||||
OAKNUT_DETECT_CAP(CRC32, 7) // HWCAP_CRC32
|
||||
OAKNUT_DETECT_CAP(LSE, 8) // HWCAP_ATOMICS
|
||||
OAKNUT_DETECT_CAP(FP16Conv, 9, 10) // HWCAP_FPHP && HWCAP_ASIMDHP
|
||||
OAKNUT_DETECT_CAP(FP16, 9, 10) // HWCAP_FPHP && HWCAP_ASIMDHP
|
||||
// HWCAP_CPUID (11)
|
||||
OAKNUT_DETECT_CAP(RDM, 12) // HWCAP_ASIMDRDM
|
||||
OAKNUT_DETECT_CAP(JSCVT, 13) // HWCAP_JSCVT
|
||||
OAKNUT_DETECT_CAP(FCMA, 14) // HWCAP_FCMA
|
||||
OAKNUT_DETECT_CAP(LRCPC, 15) // HWCAP_LRCPC
|
||||
OAKNUT_DETECT_CAP(DPB, 16) // HWCAP_DCPOP
|
||||
OAKNUT_DETECT_CAP(SHA3, 17) // HWCAP_SHA3
|
||||
OAKNUT_DETECT_CAP(SM3, 18) // HWCAP_SM3
|
||||
OAKNUT_DETECT_CAP(SM4, 19) // HWCAP_SM4
|
||||
OAKNUT_DETECT_CAP(DotProd, 20) // HWCAP_ASIMDDP
|
||||
OAKNUT_DETECT_CAP(SHA512, 21) // HWCAP_SHA512
|
||||
OAKNUT_DETECT_CAP(SVE, 22) // HWCAP_SVE
|
||||
OAKNUT_DETECT_CAP(FHM, 23) // HWCAP_ASIMDFHM
|
||||
OAKNUT_DETECT_CAP(DIT, 24) // HWCAP_DIT
|
||||
OAKNUT_DETECT_CAP(LSE2, 25) // HWCAP_USCAT
|
||||
OAKNUT_DETECT_CAP(LRCPC2, 26) // HWCAP_ILRCPC
|
||||
OAKNUT_DETECT_CAP(FlagM, 27) // HWCAP_FLAGM
|
||||
OAKNUT_DETECT_CAP(SSBS, 28) // HWCAP_SSBS
|
||||
OAKNUT_DETECT_CAP(SB, 29) // HWCAP_SB
|
||||
OAKNUT_DETECT_CAP(PACA, 30) // HWCAP_PACA
|
||||
OAKNUT_DETECT_CAP(PACG, 31) // HWCAP_PACG
|
||||
|
||||
OAKNUT_DETECT_CAP2(DPB2, 0) // HWCAP2_DCPODP
|
||||
OAKNUT_DETECT_CAP2(SVE2, 1) // HWCAP2_SVE2
|
||||
OAKNUT_DETECT_CAP2(SVE_AES, 2) // HWCAP2_SVEAES
|
||||
OAKNUT_DETECT_CAP2(SVE_PMULL128, 3) // HWCAP2_SVEPMULL
|
||||
OAKNUT_DETECT_CAP2(SVE_BITPERM, 4) // HWCAP2_SVEBITPERM
|
||||
OAKNUT_DETECT_CAP2(SVE_SHA3, 5) // HWCAP2_SVESHA3
|
||||
OAKNUT_DETECT_CAP2(SVE_SM4, 6) // HWCAP2_SVESM4
|
||||
OAKNUT_DETECT_CAP2(FlagM2, 7) // HWCAP2_FLAGM2
|
||||
OAKNUT_DETECT_CAP2(FRINTTS, 8) // HWCAP2_FRINT
|
||||
OAKNUT_DETECT_CAP2(SVE_I8MM, 9) // HWCAP2_SVEI8MM
|
||||
OAKNUT_DETECT_CAP2(SVE_F32MM, 10) // HWCAP2_SVEF32MM
|
||||
OAKNUT_DETECT_CAP2(SVE_F64MM, 11) // HWCAP2_SVEF64MM
|
||||
OAKNUT_DETECT_CAP2(SVE_BF16, 12) // HWCAP2_SVEBF16
|
||||
OAKNUT_DETECT_CAP2(I8MM, 13) // HWCAP2_I8MM
|
||||
OAKNUT_DETECT_CAP2(BF16, 14) // HWCAP2_BF16
|
||||
OAKNUT_DETECT_CAP2(DGH, 15) // HWCAP2_DGH
|
||||
OAKNUT_DETECT_CAP2(RNG, 16) // HWCAP2_RNG
|
||||
OAKNUT_DETECT_CAP2(BTI, 17) // HWCAP2_BTI
|
||||
OAKNUT_DETECT_CAP2(MTE, 18) // HWCAP2_MTE
|
||||
OAKNUT_DETECT_CAP2(ECV, 19) // HWCAP2_ECV
|
||||
OAKNUT_DETECT_CAP2(AFP, 20) // HWCAP2_AFP
|
||||
OAKNUT_DETECT_CAP2(RPRES, 21) // HWCAP2_RPRES
|
||||
OAKNUT_DETECT_CAP2(MTE3, 22) // HWCAP2_MTE3
|
||||
OAKNUT_DETECT_CAP2(SME, 23) // HWCAP2_SME
|
||||
OAKNUT_DETECT_CAP2(SME_I16I64, 24) // HWCAP2_SME_I16I64
|
||||
OAKNUT_DETECT_CAP2(SME_F64F64, 25) // HWCAP2_SME_F64F64
|
||||
OAKNUT_DETECT_CAP2(SME_I8I32, 26) // HWCAP2_SME_I8I32
|
||||
OAKNUT_DETECT_CAP2(SME_F16F32, 27) // HWCAP2_SME_F16F32
|
||||
OAKNUT_DETECT_CAP2(SME_B16F32, 28) // HWCAP2_SME_B16F32
|
||||
OAKNUT_DETECT_CAP2(SME_F32F32, 29) // HWCAP2_SME_F32F32
|
||||
OAKNUT_DETECT_CAP2(SME_FA64, 30) // HWCAP2_SME_FA64
|
||||
OAKNUT_DETECT_CAP2(WFxT, 31) // HWCAP2_WFXT
|
||||
OAKNUT_DETECT_CAP2(EBF16, 32) // HWCAP2_EBF16
|
||||
OAKNUT_DETECT_CAP2(SVE_EBF16, 33) // HWCAP2_SVE_EBF16
|
||||
OAKNUT_DETECT_CAP2(CSSC, 34) // HWCAP2_CSSC
|
||||
OAKNUT_DETECT_CAP2(RPRFM, 35) // HWCAP2_RPRFM
|
||||
OAKNUT_DETECT_CAP2(SVE2p1, 36) // HWCAP2_SVE2P1
|
||||
OAKNUT_DETECT_CAP2(SME2, 37) // HWCAP2_SME2
|
||||
OAKNUT_DETECT_CAP2(SME2p1, 38) // HWCAP2_SME2P1
|
||||
OAKNUT_DETECT_CAP2(SME_I16I32, 39) // HWCAP2_SME_I16I32
|
||||
OAKNUT_DETECT_CAP2(SME_BI32I32, 40) // HWCAP2_SME_BI32I32
|
||||
OAKNUT_DETECT_CAP2(SME_B16B16, 41) // HWCAP2_SME_B16B16
|
||||
OAKNUT_DETECT_CAP2(SME_F16F16, 42) // HWCAP2_SME_F16F16
|
||||
OAKNUT_DETECT_CAP2(MOPS, 43) // HWCAP2_MOPS
|
||||
OAKNUT_DETECT_CAP2(HBC, 44) // HWCAP2_HBC
|
||||
|
||||
#undef OAKNUT_DETECT_CAP
|
||||
#undef OAKNUT_DETECT_CAP2
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
CpuFeatures detect_features_via_id_registers(id::IdRegisters regs)
|
||||
{
|
||||
CpuFeatures result;
|
||||
|
||||
if (regs.pfr0.FP() >= 0)
|
||||
result |= CpuFeatures{CpuFeature::FP};
|
||||
if (regs.pfr0.AdvSIMD() >= 0)
|
||||
result |= CpuFeatures{CpuFeature::ASIMD};
|
||||
if (regs.isar0.AES() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::AES};
|
||||
if (regs.isar0.AES() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::PMULL};
|
||||
if (regs.isar0.SHA1() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SHA1};
|
||||
if (regs.isar0.SHA2() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SHA256};
|
||||
if (regs.isar0.CRC32() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::CRC32};
|
||||
if (regs.isar0.Atomic() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::LSE};
|
||||
if (regs.pfr0.FP() >= 1 && regs.pfr0.AdvSIMD() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::FP16Conv, CpuFeature::FP16};
|
||||
if (regs.isar0.RDM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::RDM};
|
||||
if (regs.isar1.JSCVT() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::JSCVT};
|
||||
if (regs.isar1.FCMA() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::FCMA};
|
||||
if (regs.isar1.LRCPC() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::LRCPC};
|
||||
if (regs.isar1.DPB() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::DPB};
|
||||
if (regs.isar0.SHA3() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SHA3};
|
||||
if (regs.isar0.SM3() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SM3};
|
||||
if (regs.isar0.SM4() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SM4};
|
||||
if (regs.isar0.DP() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::DotProd};
|
||||
if (regs.isar0.SHA2() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::SHA512};
|
||||
if (regs.pfr0.SVE() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE};
|
||||
if (regs.isar0.FHM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::FHM};
|
||||
if (regs.pfr0.DIT() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::DIT};
|
||||
if (regs.mmfr2.AT() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::LSE2};
|
||||
if (regs.isar1.LRCPC() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::LRCPC2};
|
||||
if (regs.isar0.TS() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::FlagM};
|
||||
if (regs.pfr1.SSBS() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::SSBS};
|
||||
if (regs.isar1.SB() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SB};
|
||||
if (regs.isar1.APA() >= 1 || regs.isar1.API() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::PACA};
|
||||
if (regs.isar1.GPA() >= 1 || regs.isar1.GPI() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::PACG};
|
||||
if (regs.isar1.DPB() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::DPB2};
|
||||
if (regs.zfr0.SVEver() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE2};
|
||||
if (regs.zfr0.AES() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_AES};
|
||||
if (regs.zfr0.AES() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::SVE_PMULL128};
|
||||
if (regs.zfr0.BitPerm() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_BITPERM};
|
||||
if (regs.zfr0.SHA3() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_SHA3};
|
||||
if (regs.zfr0.SM4() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_SM4};
|
||||
if (regs.isar0.TS() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::FlagM2};
|
||||
if (regs.isar1.FRINTTS() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::FRINTTS};
|
||||
if (regs.zfr0.I8MM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_I8MM};
|
||||
if (regs.zfr0.F32MM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_F32MM};
|
||||
if (regs.zfr0.F64MM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_F64MM};
|
||||
if (regs.zfr0.BF16() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SVE_BF16};
|
||||
if (regs.isar1.I8MM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::I8MM};
|
||||
if (regs.isar1.BF16() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::BF16};
|
||||
if (regs.isar1.DGH() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::DGH};
|
||||
if (regs.isar0.RNDR() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::RNG};
|
||||
if (regs.pfr1.BT() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::BTI};
|
||||
if (regs.pfr1.MTE() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::MTE};
|
||||
if (regs.mmfr0.ECV() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::ECV};
|
||||
if (regs.mmfr1.AFP() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::AFP};
|
||||
if (regs.isar2.RPRES() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::RPRES};
|
||||
if (regs.pfr1.MTE() >= 3)
|
||||
result |= CpuFeatures{CpuFeature::MTE3};
|
||||
if (regs.pfr1.SME() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SME};
|
||||
if (regs.smfr0.I16I64() == 0b1111)
|
||||
result |= CpuFeatures{CpuFeature::SME_I16I64};
|
||||
if (regs.smfr0.F64F64() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_F64F64};
|
||||
if (regs.smfr0.I8I32() == 0b1111)
|
||||
result |= CpuFeatures{CpuFeature::SME_I8I32};
|
||||
if (regs.smfr0.F16F32() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_F16F32};
|
||||
if (regs.smfr0.B16F32() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_B16F32};
|
||||
if (regs.smfr0.F32F32() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_F32F32};
|
||||
if (regs.smfr0.FA64() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_FA64};
|
||||
if (regs.isar2.WFxT() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::WFxT};
|
||||
if (regs.isar1.BF16() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::EBF16};
|
||||
if (regs.zfr0.BF16() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::SVE_EBF16};
|
||||
if (regs.isar2.CSSC() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::CSSC};
|
||||
if (regs.isar2.RPRFM() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::RPRFM};
|
||||
if (regs.zfr0.SVEver() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::SVE2p1};
|
||||
if (regs.smfr0.SMEver() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::SME2};
|
||||
if (regs.smfr0.SMEver() >= 2)
|
||||
result |= CpuFeatures{CpuFeature::SME2p1};
|
||||
if (regs.smfr0.I16I32() == 0b0101)
|
||||
result |= CpuFeatures{CpuFeature::SME_I16I32};
|
||||
if (regs.smfr0.BI32I32() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_BI32I32};
|
||||
if (regs.smfr0.B16B16() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_B16B16};
|
||||
if (regs.smfr0.F16F16() == 0b1)
|
||||
result |= CpuFeatures{CpuFeature::SME_F16F16};
|
||||
if (regs.isar2.MOPS() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::MOPS};
|
||||
if (regs.isar2.BC() >= 1)
|
||||
result |= CpuFeatures{CpuFeature::HBC};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/feature_detection_hwcaps.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
#include "oaknut/feature_detection/read_id_registers_directly.hpp"
|
||||
|
||||
#ifndef AT_HWCAP
|
||||
# define AT_HWCAP 16
|
||||
#endif
|
||||
#ifndef AT_HWCAP2
|
||||
# define AT_HWCAP2 26
|
||||
#endif
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
inline CpuFeatures detect_features_via_hwcap()
|
||||
{
|
||||
const unsigned long hwcap = ::getauxval(AT_HWCAP);
|
||||
const unsigned long hwcap2 = ::getauxval(AT_HWCAP2);
|
||||
return detect_features_via_hwcap(hwcap, hwcap2);
|
||||
}
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
return detect_features_via_hwcap();
|
||||
}
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers()
|
||||
{
|
||||
constexpr unsigned long hwcap_cpuid = (1 << 11);
|
||||
if (::getauxval(AT_HWCAP) & hwcap_cpuid) {
|
||||
return id::read_id_registers_directly();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <aarch64/armreg.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/feature_detection_hwcaps.hpp"
|
||||
#include "oaknut/feature_detection/feature_detection_idregs.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers(std::size_t core_index)
|
||||
{
|
||||
const std::string path = "machdep.cpu" + std::to_string(core_index) + ".cpu_id";
|
||||
|
||||
aarch64_sysctl_cpu_id id;
|
||||
std::size_t id_len = sizeof id;
|
||||
|
||||
if (sysctlbyname(path.c_str(), &id, &id_len, nullptr, 0) < 0)
|
||||
return std::nullopt;
|
||||
|
||||
return id::IdRegisters{
|
||||
id.ac_midr,
|
||||
id::Pfr0Register{id.ac_aa64pfr0},
|
||||
id::Pfr1Register{id.ac_aa64pfr1},
|
||||
id::Pfr2Register{0},
|
||||
id::Zfr0Register{id.ac_aa64zfr0},
|
||||
id::Smfr0Register{0},
|
||||
id::Isar0Register{id.ac_aa64isar0},
|
||||
id::Isar1Register{id.ac_aa64isar1},
|
||||
id::Isar2Register{0},
|
||||
id::Isar3Register{0},
|
||||
id::Mmfr0Register{id.ac_aa64mmfr0},
|
||||
id::Mmfr1Register{id.ac_aa64mmfr1},
|
||||
id::Mmfr2Register{id.ac_aa64mmfr2},
|
||||
id::Mmfr3Register{0},
|
||||
id::Mmfr4Register{0},
|
||||
};
|
||||
}
|
||||
|
||||
inline std::size_t get_core_count()
|
||||
{
|
||||
int result = 0;
|
||||
size_t result_size = sizeof(result);
|
||||
const std::array<int, 2> mib{CTL_HW, HW_NCPU};
|
||||
if (sysctl(mib.data(), mib.size(), &result, &result_size, nullptr, 0) < 0)
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
std::optional<CpuFeatures> result;
|
||||
|
||||
const std::size_t core_count = get_core_count();
|
||||
for (std::size_t core_index = 0; core_index < core_count; core_index++) {
|
||||
if (const std::optional<id::IdRegisters> id_regs = read_id_registers(core_index)) {
|
||||
const CpuFeatures current_features = detect_features_via_id_registers(*id_regs);
|
||||
if (result) {
|
||||
result = *result & current_features;
|
||||
} else {
|
||||
result = current_features;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.value_or(CpuFeatures{});
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/feature_detection_hwcaps.hpp"
|
||||
#include "oaknut/feature_detection/feature_detection_idregs.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline std::uint64_t read_id_register(int index)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
size_t result_size = sizeof(result);
|
||||
std::array<int, 2> mib{CTL_MACHDEP, index};
|
||||
if (sysctl(mib.data(), mib.size(), &result, &result_size, nullptr, 0) < 0)
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers()
|
||||
{
|
||||
// See OpenBSD source: sys/arch/arm64/include/cpu.h
|
||||
|
||||
return id::IdRegisters{
|
||||
std::nullopt, // No easy way of getting MIDR_EL1 other than reading /proc/cpu
|
||||
id::Pfr0Register{detail::read_id_register(8)}, // CPU_ID_AA64PFR0
|
||||
id::Pfr1Register{detail::read_id_register(9)}, // CPU_ID_AA64PFR1
|
||||
id::Pfr2Register{0},
|
||||
id::Zfr0Register{detail::read_id_register(11)}, // CPU_ID_AA64ZFR0
|
||||
id::Smfr0Register{detail::read_id_register(10)}, // CPU_ID_AA64SMFR0
|
||||
id::Isar0Register{detail::read_id_register(2)}, // CPU_ID_AA64ISAR0
|
||||
id::Isar1Register{detail::read_id_register(3)}, // CPU_ID_AA64ISAR1
|
||||
id::Isar2Register{detail::read_id_register(4)}, // CPU_ID_AA64ISAR2
|
||||
id::Isar3Register{0},
|
||||
id::Mmfr0Register{detail::read_id_register(5)}, // CPU_ID_AA64MMFR0
|
||||
id::Mmfr1Register{detail::read_id_register(6)}, // CPU_ID_AA64MMFR1
|
||||
id::Mmfr2Register{detail::read_id_register(7)}, // CPU_ID_AA64MMFR2
|
||||
id::Mmfr3Register{0},
|
||||
id::Mmfr4Register{0},
|
||||
};
|
||||
}
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
return detect_features_via_id_registers(*read_id_registers());
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include <processthreadsapi.h>
|
||||
|
||||
#include "oaknut/feature_detection/cpu_feature.hpp"
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline std::optional<std::uint64_t> read_registry_hklm(const std::string& subkey, const std::string& name)
|
||||
{
|
||||
std::uint64_t value;
|
||||
DWORD value_len = sizeof(value);
|
||||
if (::RegGetValueA(HKEY_LOCAL_MACHINE, subkey.c_str(), name.c_str(), RRF_RT_REG_QWORD, nullptr, &value, &value_len) == ERROR_SUCCESS) {
|
||||
return value;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline std::uint64_t read_id_register(std::size_t core_index, const std::string& name)
|
||||
{
|
||||
return read_registry_hklm("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" + std::to_string(core_index), "CP " + name).value_or(0);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Ref: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
|
||||
|
||||
inline CpuFeatures detect_features_via_IsProcessorFeaturePresent()
|
||||
{
|
||||
CpuFeatures result;
|
||||
|
||||
if (::IsProcessorFeaturePresent(30)) // PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE
|
||||
result |= CpuFeatures{CpuFeature::AES, CpuFeature::PMULL, CpuFeature::SHA1, CpuFeature::SHA256};
|
||||
if (::IsProcessorFeaturePresent(31)) // PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE
|
||||
result |= CpuFeatures{CpuFeature::CRC32};
|
||||
if (::IsProcessorFeaturePresent(34)) // PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE
|
||||
result |= CpuFeatures{CpuFeature::LSE};
|
||||
if (::IsProcessorFeaturePresent(43)) // PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE
|
||||
result |= CpuFeatures{CpuFeature::DotProd};
|
||||
if (::IsProcessorFeaturePresent(44)) // PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE
|
||||
result |= CpuFeatures{CpuFeature::JSCVT};
|
||||
if (::IsProcessorFeaturePresent(45)) // PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE
|
||||
result |= CpuFeatures{CpuFeature::LRCPC};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline CpuFeatures detect_features()
|
||||
{
|
||||
CpuFeatures result{CpuFeature::FP, CpuFeature::ASIMD};
|
||||
result |= detect_features_via_IsProcessorFeaturePresent();
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::size_t get_core_count()
|
||||
{
|
||||
::SYSTEM_INFO sys_info;
|
||||
::GetSystemInfo(&sys_info);
|
||||
return sys_info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
inline std::optional<id::IdRegisters> read_id_registers(std::size_t core_index)
|
||||
{
|
||||
return id::IdRegisters{
|
||||
detail::read_id_register(core_index, "4000"),
|
||||
id::Pfr0Register{detail::read_id_register(core_index, "4020")},
|
||||
id::Pfr1Register{detail::read_id_register(core_index, "4021")},
|
||||
id::Pfr2Register{detail::read_id_register(core_index, "4022")},
|
||||
id::Zfr0Register{detail::read_id_register(core_index, "4024")},
|
||||
id::Smfr0Register{detail::read_id_register(core_index, "4025")},
|
||||
id::Isar0Register{detail::read_id_register(core_index, "4030")},
|
||||
id::Isar1Register{detail::read_id_register(core_index, "4031")},
|
||||
id::Isar2Register{detail::read_id_register(core_index, "4032")},
|
||||
id::Isar3Register{detail::read_id_register(core_index, "4033")},
|
||||
id::Mmfr0Register{detail::read_id_register(core_index, "4038")},
|
||||
id::Mmfr1Register{detail::read_id_register(core_index, "4039")},
|
||||
id::Mmfr2Register{detail::read_id_register(core_index, "403A")},
|
||||
id::Mmfr3Register{detail::read_id_register(core_index, "403B")},
|
||||
id::Mmfr4Register{detail::read_id_register(core_index, "403C")},
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace oaknut
|
||||
@@ -0,0 +1,318 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace oaknut::id {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<std::size_t lsb>
|
||||
constexpr unsigned extract_bit(std::uint64_t value)
|
||||
{
|
||||
return (value >> lsb) & 1;
|
||||
}
|
||||
|
||||
template<std::size_t lsb>
|
||||
constexpr unsigned extract_field(std::uint64_t value)
|
||||
{
|
||||
return (value >> lsb) & 0xf;
|
||||
}
|
||||
|
||||
template<std::size_t lsb>
|
||||
constexpr signed extract_signed_field(std::uint64_t value)
|
||||
{
|
||||
return static_cast<signed>(static_cast<std::int64_t>(value << (60 - lsb)) >> 60);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct Pfr0Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr signed FP() const { return detail::extract_signed_field<16>(value); }
|
||||
constexpr signed AdvSIMD() const { return detail::extract_signed_field<20>(value); }
|
||||
constexpr unsigned GIC() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned RAS() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned SVE() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned SEL2() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned MPAM() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned AMU() const { return detail::extract_field<44>(value); }
|
||||
constexpr unsigned DIT() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned RME() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned CSV2() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned CSV3() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Pfr1Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned BT() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned SSBS() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned MTE() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned RAS_frac() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned MPAM_frac() const { return detail::extract_field<16>(value); }
|
||||
// [20:23] - reserved
|
||||
constexpr unsigned SME() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned RNDR_trap() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned CSV2_frac() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned NMI() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned MTE_frac() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned GCS() const { return detail::extract_field<44>(value); }
|
||||
constexpr unsigned THE() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned MTEX() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned DF2() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned PFAR() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Pfr2Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned MTEPERM() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned MTESTOREONLY() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned MTEFAR() const { return detail::extract_field<8>(value); }
|
||||
// [12:31] reserved
|
||||
constexpr unsigned FPMR() const { return detail::extract_field<32>(value); }
|
||||
// [36:63] reserved
|
||||
};
|
||||
|
||||
struct Zfr0Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned SVEver() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned AES() const { return detail::extract_field<4>(value); }
|
||||
// [8:15] reserved
|
||||
constexpr unsigned BitPerm() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned BF16() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned B16B16() const { return detail::extract_field<24>(value); }
|
||||
// [28:31] reserved
|
||||
constexpr unsigned SHA3() const { return detail::extract_field<32>(value); }
|
||||
// [36:39] reserved
|
||||
constexpr unsigned SM4() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned I8MM() const { return detail::extract_field<44>(value); }
|
||||
// [48:51] reserved
|
||||
constexpr unsigned F32MM() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned F64MM() const { return detail::extract_field<56>(value); }
|
||||
// [60:63] reserved
|
||||
};
|
||||
|
||||
struct Smfr0Register {
|
||||
std::uint64_t value;
|
||||
|
||||
// [0:27] reserved
|
||||
constexpr unsigned SF8DP2() const { return detail::extract_bit<28>(value); }
|
||||
constexpr unsigned SF8DP4() const { return detail::extract_bit<29>(value); }
|
||||
constexpr unsigned SF8FMA() const { return detail::extract_bit<30>(value); }
|
||||
// [31] reserved
|
||||
constexpr unsigned F32F32() const { return detail::extract_bit<32>(value); }
|
||||
constexpr unsigned BI32I32() const { return detail::extract_bit<33>(value); }
|
||||
constexpr unsigned B16F32() const { return detail::extract_bit<34>(value); }
|
||||
constexpr unsigned F16F32() const { return detail::extract_bit<35>(value); }
|
||||
constexpr unsigned I8I32() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned F8F32() const { return detail::extract_bit<40>(value); }
|
||||
constexpr unsigned F8F16() const { return detail::extract_bit<41>(value); }
|
||||
constexpr unsigned F16F16() const { return detail::extract_bit<42>(value); }
|
||||
constexpr unsigned B16B16() const { return detail::extract_bit<43>(value); }
|
||||
constexpr unsigned I16I32() const { return detail::extract_field<44>(value); }
|
||||
constexpr unsigned F64F64() const { return detail::extract_bit<48>(value); }
|
||||
// [49:51] reserved
|
||||
constexpr unsigned I16I64() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned SMEver() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned LUTv2() const { return detail::extract_bit<60>(value); }
|
||||
// [61:62] reserved
|
||||
constexpr unsigned FA64() const { return detail::extract_bit<63>(value); }
|
||||
};
|
||||
|
||||
struct Isar0Register {
|
||||
std::uint64_t value;
|
||||
|
||||
// [0:3] reserved
|
||||
constexpr unsigned AES() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned SHA1() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned SHA2() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned CRC32() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned Atomic() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned TME() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned RDM() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned SHA3() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned SM3() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned SM4() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned DP() const { return detail::extract_field<44>(value); }
|
||||
constexpr unsigned FHM() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned TS() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned TLB() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned RNDR() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Isar1Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned DPB() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned APA() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned API() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned JSCVT() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned FCMA() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned LRCPC() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned GPA() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned GPI() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned FRINTTS() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned SB() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned SPECRES() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned BF16() const { return detail::extract_field<44>(value); }
|
||||
constexpr unsigned DGH() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned I8MM() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned XS() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned LS64() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Isar2Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned WFxT() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned RPRES() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned GPA3() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned APA3() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned MOPS() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned BC() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned PAC_frac() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned CLRBHB() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned SYSREG_128() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned SYSINSTR_128() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned PRFMSLC() const { return detail::extract_field<40>(value); }
|
||||
// [44:47] reserved
|
||||
constexpr unsigned RPRFM() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned CSSC() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned LUT() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned ATS1A() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Isar3Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned CPA() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned FAMINMAX() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned TLBIW() const { return detail::extract_field<8>(value); }
|
||||
// [12:63] reserved
|
||||
};
|
||||
|
||||
struct Mmfr0Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned PARange() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned ASIDBits() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned BigEnd() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned SNSMem() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned BigEndEL0() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned TGran16() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned TGran64() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned TGran4() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned TGran16_2() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned TGran64_2() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned TGran4_2() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned ExS() const { return detail::extract_field<44>(value); }
|
||||
// [48:55] reserved
|
||||
constexpr unsigned FGT() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned ECV() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Mmfr1Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned HAFDBS() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned VMIDBits() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned VH() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned HPDS() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned LO() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned PAN() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned SpecSEI() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned XNX() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned TWED() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned ETS() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned HCX() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned AFP() const { return detail::extract_field<44>(value); }
|
||||
constexpr unsigned nTLBPA() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned TIDCP1() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned CMOW() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned ECBHB() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Mmfr2Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned CnP() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned UAO() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned LSM() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned IESB() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned VARange() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned CCIDX() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned NV() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned ST() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned AT() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned IDS() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned FWB() const { return detail::extract_field<40>(value); }
|
||||
// [44:47] reserved
|
||||
constexpr unsigned TTL() const { return detail::extract_field<48>(value); }
|
||||
constexpr unsigned BBM() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned EVT() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned E0PD() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Mmfr3Register {
|
||||
std::uint64_t value;
|
||||
|
||||
constexpr unsigned TCRX() const { return detail::extract_field<0>(value); }
|
||||
constexpr unsigned SCTLRX() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned S1PIE() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned S2PIE() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned S1POE() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned S2POE() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned AIE() const { return detail::extract_field<24>(value); }
|
||||
constexpr unsigned MEC() const { return detail::extract_field<28>(value); }
|
||||
constexpr unsigned D128() const { return detail::extract_field<32>(value); }
|
||||
constexpr unsigned D128_2() const { return detail::extract_field<36>(value); }
|
||||
constexpr unsigned SNERR() const { return detail::extract_field<40>(value); }
|
||||
constexpr unsigned ANERR() const { return detail::extract_field<44>(value); }
|
||||
// [48:51] reserved
|
||||
constexpr unsigned SDERR() const { return detail::extract_field<52>(value); }
|
||||
constexpr unsigned ADERR() const { return detail::extract_field<56>(value); }
|
||||
constexpr unsigned Spec_FPACC() const { return detail::extract_field<60>(value); }
|
||||
};
|
||||
|
||||
struct Mmfr4Register {
|
||||
std::uint64_t value;
|
||||
|
||||
// [0:3] reserved
|
||||
constexpr unsigned EIESB() const { return detail::extract_field<4>(value); }
|
||||
constexpr unsigned ASID2() const { return detail::extract_field<8>(value); }
|
||||
constexpr unsigned HACDBS() const { return detail::extract_field<12>(value); }
|
||||
constexpr unsigned FGWTE3() const { return detail::extract_field<16>(value); }
|
||||
constexpr unsigned NV_frac() const { return detail::extract_field<20>(value); }
|
||||
constexpr unsigned E2H0() const { return detail::extract_field<24>(value); }
|
||||
// [28:35] reserved
|
||||
constexpr unsigned E3DSE() const { return detail::extract_field<36>(value); }
|
||||
// [40:63] reserved
|
||||
};
|
||||
|
||||
struct IdRegisters {
|
||||
std::optional<std::uint64_t> midr;
|
||||
Pfr0Register pfr0;
|
||||
Pfr1Register pfr1;
|
||||
Pfr2Register pfr2;
|
||||
Zfr0Register zfr0;
|
||||
Smfr0Register smfr0;
|
||||
Isar0Register isar0;
|
||||
Isar1Register isar1;
|
||||
Isar2Register isar2;
|
||||
Isar3Register isar3;
|
||||
Mmfr0Register mmfr0;
|
||||
Mmfr1Register mmfr1;
|
||||
Mmfr2Register mmfr2;
|
||||
Mmfr3Register mmfr3;
|
||||
Mmfr4Register mmfr4;
|
||||
};
|
||||
|
||||
} // namespace oaknut::id
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "oaknut/feature_detection/id_registers.hpp"
|
||||
|
||||
namespace oaknut::id {
|
||||
|
||||
inline IdRegisters read_id_registers_directly()
|
||||
{
|
||||
std::uint64_t midr, pfr0, pfr1, pfr2, isar0, isar1, isar2, isar3, mmfr0, mmfr1, mmfr2, mmfr3, mmfr4, zfr0, smfr0;
|
||||
|
||||
#define OAKNUT_READ_REGISTER(reg, var) \
|
||||
__asm__("mrs %0, " #reg \
|
||||
: "=r"(var))
|
||||
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c0_0, midr);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c4_0, pfr0);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c4_1, pfr1);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c4_2, pfr2);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c4_4, zfr0);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c4_5, smfr0);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c6_0, isar0);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c6_1, isar1);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c6_2, isar2);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c6_3, isar3);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c7_0, mmfr0);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c7_1, mmfr1);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c7_2, mmfr2);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c7_3, mmfr3);
|
||||
OAKNUT_READ_REGISTER(s3_0_c0_c7_4, mmfr4);
|
||||
|
||||
#undef OAKNUT_READ_ID_REGISTER
|
||||
|
||||
return IdRegisters{
|
||||
midr,
|
||||
Pfr0Register{pfr0},
|
||||
Pfr1Register{pfr1},
|
||||
Pfr2Register{pfr2},
|
||||
Zfr0Register{zfr0},
|
||||
Smfr0Register{smfr0},
|
||||
Isar0Register{isar0},
|
||||
Isar1Register{isar1},
|
||||
Isar2Register{isar2},
|
||||
Isar3Register{isar3},
|
||||
Mmfr0Register{mmfr0},
|
||||
Mmfr1Register{mmfr1},
|
||||
Mmfr2Register{mmfr2},
|
||||
Mmfr3Register{mmfr3},
|
||||
Mmfr4Register{mmfr4},
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace oaknut::id
|
||||
Reference in New Issue
Block a user