/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cpu

/*
#include <sys/auxv.h>
#define HWCAP_CPUID	(1 << 11)

unsigned long gethwcap() {
	return getauxval(AT_HWCAP);
}
unsigned long gethwcap2() {
	return getauxval(AT_HWCAP2);
}
*/
import "C"

/*
all special features for arm64 should be defined here; canonical list:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/include/uapi/asm/hwcap.h
*/
const (
	/* extension instructions */
	CPU_ARM64_FEATURE_FP = 1 << iota
	CPU_ARM64_FEATURE_ASIMD
	CPU_ARM64_FEATURE_EVTSTRM
	CPU_ARM64_FEATURE_AES
	CPU_ARM64_FEATURE_PMULL
	CPU_ARM64_FEATURE_SHA1
	CPU_ARM64_FEATURE_SHA2
	CPU_ARM64_FEATURE_CRC32
	CPU_ARM64_FEATURE_ATOMICS
	CPU_ARM64_FEATURE_FPHP
	CPU_ARM64_FEATURE_ASIMDHP
	CPU_ARM64_FEATURE_CPUID
	CPU_ARM64_FEATURE_ASIMDRDM
	CPU_ARM64_FEATURE_JSCVT
	CPU_ARM64_FEATURE_FCMA
	CPU_ARM64_FEATURE_LRCPC
	CPU_ARM64_FEATURE_DCPOP
	CPU_ARM64_FEATURE_SHA3
	CPU_ARM64_FEATURE_SM3
	CPU_ARM64_FEATURE_SM4
	CPU_ARM64_FEATURE_ASIMDDP
	CPU_ARM64_FEATURE_SHA512
	CPU_ARM64_FEATURE_SVE
	CPU_ARM64_FEATURE_ASIMDFHM
	CPU_ARM64_FEATURE_DIT
	CPU_ARM64_FEATURE_USCAT
	CPU_ARM64_FEATURE_ILRCPC
	CPU_ARM64_FEATURE_FLAGM
	CPU_ARM64_FEATURE_SSBS
	CPU_ARM64_FEATURE_SB
	CPU_ARM64_FEATURE_PACA
	CPU_ARM64_FEATURE_PACG
)

const (
	/* extension instructions */
	CPU_ARM64_FEATURE2_DCPODP = 1 << iota
	CPU_ARM64_FEATURE2_SVE2
	CPU_ARM64_FEATURE2_SVEAES
	CPU_ARM64_FEATURE2_SVEPMULL
	CPU_ARM64_FEATURE2_SVEBITPERM
	CPU_ARM64_FEATURE2_SVESHA3
	CPU_ARM64_FEATURE2_SVESM4
	CPU_ARM64_FEATURE2_FLAGM2
	CPU_ARM64_FEATURE2_FRINT
	CPU_ARM64_FEATURE2_SVEI8MM
	CPU_ARM64_FEATURE2_SVEF32MM
	CPU_ARM64_FEATURE2_SVEF64MM
	CPU_ARM64_FEATURE2_SVEBF16
	CPU_ARM64_FEATURE2_I8MM
	CPU_ARM64_FEATURE2_BF16
	CPU_ARM64_FEATURE2_DGH
	CPU_ARM64_FEATURE2_RNG
	CPU_ARM64_FEATURE2_BTI
	CPU_ARM64_FEATURE2_MTE
	CPU_ARM64_FEATURE2_ECV
	CPU_ARM64_FEATURE2_AFP
	CPU_ARM64_FEATURE2_RPRES
	CPU_ARM64_FEATURE2_MTE3
	CPU_ARM64_FEATURE2_SME
	CPU_ARM64_FEATURE2_SME_I16I64
	CPU_ARM64_FEATURE2_SME_F64F64
	CPU_ARM64_FEATURE2_SME_I8I32
	CPU_ARM64_FEATURE2_SME_F16F32
	CPU_ARM64_FEATURE2_SME_B16F32
	CPU_ARM64_FEATURE2_SME_F32F32
	CPU_ARM64_FEATURE2_SME_FA64
	CPU_ARM64_FEATURE2_WFXT
	CPU_ARM64_FEATURE2_EBF16
	CPU_ARM64_FEATURE2_SVE_EBF16
)

var flagNames_arm64 = map[uint64]string{
	CPU_ARM64_FEATURE_FP:       "FP",
	CPU_ARM64_FEATURE_ASIMD:    "ASIMD",
	CPU_ARM64_FEATURE_EVTSTRM:  "EVTSTRM",
	CPU_ARM64_FEATURE_AES:      "AES",
	CPU_ARM64_FEATURE_PMULL:    "PMULL",
	CPU_ARM64_FEATURE_SHA1:     "SHA1",
	CPU_ARM64_FEATURE_SHA2:     "SHA2",
	CPU_ARM64_FEATURE_CRC32:    "CRC32",
	CPU_ARM64_FEATURE_ATOMICS:  "ATOMICS",
	CPU_ARM64_FEATURE_FPHP:     "FPHP",
	CPU_ARM64_FEATURE_ASIMDHP:  "ASIMDHP",
	CPU_ARM64_FEATURE_CPUID:    "CPUID",
	CPU_ARM64_FEATURE_ASIMDRDM: "ASIMDRDM",
	CPU_ARM64_FEATURE_JSCVT:    "JSCVT",
	CPU_ARM64_FEATURE_FCMA:     "FCMA",
	CPU_ARM64_FEATURE_LRCPC:    "LRCPC",
	CPU_ARM64_FEATURE_DCPOP:    "DCPOP",
	CPU_ARM64_FEATURE_SHA3:     "SHA3",
	CPU_ARM64_FEATURE_SM3:      "SM3",
	CPU_ARM64_FEATURE_SM4:      "SM4",
	CPU_ARM64_FEATURE_ASIMDDP:  "ASIMDDP",
	CPU_ARM64_FEATURE_SHA512:   "SHA512",
	CPU_ARM64_FEATURE_SVE:      "SVE",
	CPU_ARM64_FEATURE_ASIMDFHM: "ASIMDFHM",
	CPU_ARM64_FEATURE_DIT:      "DIT",
	CPU_ARM64_FEATURE_USCAT:    "USCAT",
	CPU_ARM64_FEATURE_ILRCPC:   "ILRCPC",
	CPU_ARM64_FEATURE_FLAGM:    "FLAGM",
	CPU_ARM64_FEATURE_SSBS:     "SSBS",
	CPU_ARM64_FEATURE_SB:       "SB",
	CPU_ARM64_FEATURE_PACA:     "PACA",
	CPU_ARM64_FEATURE_PACG:     "PACG",
}

var flag2Names_arm64 = map[uint64]string{
	CPU_ARM64_FEATURE2_DCPODP:     "DCPODP",
	CPU_ARM64_FEATURE2_SVE2:       "SVE2",
	CPU_ARM64_FEATURE2_SVEAES:     "SVEAES",
	CPU_ARM64_FEATURE2_SVEPMULL:   "SVEPMULL",
	CPU_ARM64_FEATURE2_SVEBITPERM: "SVEBITPERM",
	CPU_ARM64_FEATURE2_SVESHA3:    "SVESHA3",
	CPU_ARM64_FEATURE2_SVESM4:     "SVESM4",
	CPU_ARM64_FEATURE2_FLAGM2:     "FLAGM2",
	CPU_ARM64_FEATURE2_FRINT:      "FRINT",
	CPU_ARM64_FEATURE2_SVEI8MM:    "SVEI8MM",
	CPU_ARM64_FEATURE2_SVEF32MM:   "SVEF32MM",
	CPU_ARM64_FEATURE2_SVEF64MM:   "SVEF64MM",
	CPU_ARM64_FEATURE2_SVEBF16:    "SVEBF16",
	CPU_ARM64_FEATURE2_I8MM:       "I8MM",
	CPU_ARM64_FEATURE2_BF16:       "BF16",
	CPU_ARM64_FEATURE2_DGH:        "DGH",
	CPU_ARM64_FEATURE2_RNG:        "RNG",
	CPU_ARM64_FEATURE2_BTI:        "BTI",
	CPU_ARM64_FEATURE2_MTE:        "MTE",
	CPU_ARM64_FEATURE2_ECV:        "ECV",
	CPU_ARM64_FEATURE2_AFP:        "AFP",
	CPU_ARM64_FEATURE2_RPRES:      "RPRES",
	CPU_ARM64_FEATURE2_MTE3:       "MTE3",
	CPU_ARM64_FEATURE2_SME:        "SME",
	CPU_ARM64_FEATURE2_SME_I16I64: "SMEI16I64",
	CPU_ARM64_FEATURE2_SME_F64F64: "SMEF64F64",
	CPU_ARM64_FEATURE2_SME_I8I32:  "SMEI8I32",
	CPU_ARM64_FEATURE2_SME_F16F32: "SMEF16F32",
	CPU_ARM64_FEATURE2_SME_B16F32: "SMEB16F32",
	CPU_ARM64_FEATURE2_SME_F32F32: "SMEF32F32",
	CPU_ARM64_FEATURE2_SME_FA64:   "SMEFA64",
	CPU_ARM64_FEATURE2_WFXT:       "WFXT",
	CPU_ARM64_FEATURE2_EBF16:      "EBF16",
	CPU_ARM64_FEATURE2_SVE_EBF16:  "SVEEBF16",
}

func getCpuidFlags() []string {
	r := make([]string, 0, 20)
	hwcap := uint64(C.gethwcap())
	hwcap2 := uint64(C.gethwcap2())
	for i := uint(0); i < 64; i++ {
		key := uint64(1 << i)
		val, ok := flagNames_arm64[key]
		if hwcap&key != 0 && ok {
			r = append(r, val)
		}
	}
	for i := uint(0); i < 64; i++ {
		key := uint64(1 << i)
		val, ok := flag2Names_arm64[key]
		if hwcap2&key != 0 && ok {
			r = append(r, val)
		}
	}
	return r
}