//! aarch64 架构以及一些配置定义
#![no_std]
#![allow(non_upper_case_globals)]
#![allow(missing_docs)]

use core::mem::{offset_of, size_of};

/// aarch64 pt 寄存器
///
/// 保证 C ABI 兼容并且 16 字节对齐
#[repr(C)]
pub struct Ptregs {
    regs_x0: u64,
    regs_x1: u64,
    regs_x2: u64,
    regs_x3: u64,
    regs_x4: u64,
    regs_x5: u64,
    regs_x6: u64,
    regs_x7: u64,
    regs_x8: u64,
    regs_x9: u64,
    regs_x10: u64,
    regs_x11: u64,
    regs_x12: u64,
    regs_x13: u64,
    regs_x14: u64,
    regs_x15: u64,
    regs_x16: u64,
    regs_x17: u64,
    regs_x18: u64,
    regs_x19: u64,
    regs_x20: u64,
    regs_x21: u64,
    regs_x22: u64,
    regs_x23: u64,
    regs_x24: u64,
    regs_x25: u64,
    regs_x26: u64,
    regs_x27: u64,
    regs_x28: u64,
    regs_x29: u64,
    regs_x30: u64,
    sp: u64,
    pc: u64,
    pstate: u64,

    orig_x0: u64,
    syscallno: i32,
    unused2: u32,
    orig_addr_limit: u64,
    unused: u64,
    stackframe: [u64; 2],
}

impl Ptregs {
    /// 获取`pc`
    pub fn get_pc(&self) -> u64 {
        self.pc
    }

    /// 获取`pstate`
    pub fn get_pstate(&self) -> u64 {
        self.pstate
    }

    /// 获取`orig_addr_limit`
    pub fn get_orig_addr_limit(&self) -> u64 {
        self.orig_addr_limit
    }
}

/// x0 寄存器偏移
pub const S_X0: usize = offset_of!(Ptregs, regs_x0);
/// x1 寄存器偏移
pub const S_X1: usize = offset_of!(Ptregs, regs_x1);
/// x2 寄存器偏移
pub const S_X2: usize = offset_of!(Ptregs, regs_x2);
/// x3 寄存器偏移
pub const S_X3: usize = offset_of!(Ptregs, regs_x3);
/// x4 寄存器偏移
pub const S_X4: usize = offset_of!(Ptregs, regs_x4);
/// x5 寄存器偏移
pub const S_X5: usize = offset_of!(Ptregs, regs_x5);
/// x6 寄存器偏移
pub const S_X6: usize = offset_of!(Ptregs, regs_x6);
/// x7 寄存器偏移
pub const S_X7: usize = offset_of!(Ptregs, regs_x7);
/// x8 寄存器偏移
pub const S_X8: usize = offset_of!(Ptregs, regs_x8);
/// x9 寄存器偏移
pub const S_X9: usize = offset_of!(Ptregs, regs_x9);
/// x10 寄存器偏移
pub const S_X10: usize = offset_of!(Ptregs, regs_x10);
/// x11 寄存器偏移
pub const S_X11: usize = offset_of!(Ptregs, regs_x11);
/// x12 寄存器偏移
pub const S_X12: usize = offset_of!(Ptregs, regs_x12);
/// x13 寄存器偏移
pub const S_X13: usize = offset_of!(Ptregs, regs_x13);
/// x14 寄存器偏移
pub const S_X14: usize = offset_of!(Ptregs, regs_x14);
/// x15 寄存器偏移
pub const S_X15: usize = offset_of!(Ptregs, regs_x15);
/// x16 寄存器偏移
pub const S_X16: usize = offset_of!(Ptregs, regs_x16);
/// x17 寄存器偏移
pub const S_X17: usize = offset_of!(Ptregs, regs_x17);
/// x18 寄存器偏移
pub const S_X18: usize = offset_of!(Ptregs, regs_x18);
/// x19 寄存器偏移
pub const S_X19: usize = offset_of!(Ptregs, regs_x19);
/// x20 寄存器偏移
pub const S_X20: usize = offset_of!(Ptregs, regs_x20);
/// x21 寄存器偏移
pub const S_X21: usize = offset_of!(Ptregs, regs_x21);
/// x22 寄存器偏移
pub const S_X22: usize = offset_of!(Ptregs, regs_x22);
/// x23 寄存器偏移
pub const S_X23: usize = offset_of!(Ptregs, regs_x23);
/// x24 寄存器偏移
pub const S_X24: usize = offset_of!(Ptregs, regs_x24);
/// x25 寄存器偏移
pub const S_X25: usize = offset_of!(Ptregs, regs_x25);
/// x26 寄存器偏移
pub const S_X26: usize = offset_of!(Ptregs, regs_x26);
/// x27 寄存器偏移
pub const S_X27: usize = offset_of!(Ptregs, regs_x27);
/// x28 寄存器偏移
pub const S_X28: usize = offset_of!(Ptregs, regs_x28);
/// x29 寄存器偏移
pub const S_X29: usize = offset_of!(Ptregs, regs_x29);
/// lr 寄存器偏移
pub const S_LR: usize = offset_of!(Ptregs, regs_x30);
/// sp 寄存器偏移
pub const S_SP: usize = offset_of!(Ptregs, sp);
/// pc 寄存器偏移
pub const S_PC: usize = offset_of!(Ptregs, pc);
/// pstate 寄存器偏移
pub const S_PSTATE: usize = offset_of!(Ptregs, pstate);
/// 原始 x0 数据存储偏移
pub const S_ORIG_X0: usize = offset_of!(Ptregs, orig_x0);
/// 原始系统调用号偏移
pub const S_SYSCALLNO: usize = offset_of!(Ptregs, syscallno);
/// 原始地址限制存储偏移
pub const S_ORIG_ADDR_LIMIT: usize = offset_of!(Ptregs, orig_addr_limit);
/// 栈帧地址偏移
pub const S_STACKFRAME: usize = offset_of!(Ptregs, stackframe);
/// 帧栈总大小
pub const S_FRAME_SIZE: usize = size_of::<Ptregs>();

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test() {
        assert_eq!(S_X0, 0);
        assert_eq!(S_X1, 8);
        assert_eq!(S_X2, 16);
        assert_eq!(S_X3, 24);
        assert_eq!(S_X4, 32);
        assert_eq!(S_X5, 40);
        assert_eq!(S_X6, 48);
        assert_eq!(S_X7, 56);
        assert_eq!(S_X8, 64);
        assert_eq!(S_X9, 72);
        assert_eq!(S_X10, 80);
        assert_eq!(S_X11, 88);
        assert_eq!(S_X12, 96);
        assert_eq!(S_X13, 104);
        assert_eq!(S_X14, 112);
        assert_eq!(S_X15, 120);
        assert_eq!(S_X16, 128);
        assert_eq!(S_X17, 136);
        assert_eq!(S_X18, 144);
        assert_eq!(S_X19, 152);
        assert_eq!(S_X20, 160);
        assert_eq!(S_X21, 168);
        assert_eq!(S_X22, 176);
        assert_eq!(S_X23, 184);
        assert_eq!(S_X24, 192);
        assert_eq!(S_X25, 200);
        assert_eq!(S_X26, 208);
        assert_eq!(S_X27, 216);
        assert_eq!(S_X28, 224);
        assert_eq!(S_X29, 232);
        assert_eq!(S_LR, 240);
        assert_eq!(S_SP, 248);
        assert_eq!(S_PC, 256);
        assert_eq!(S_PSTATE, 264);
        assert_eq!(S_ORIG_X0, 272);
        assert_eq!(S_SYSCALLNO, 280);
        assert_eq!(S_ORIG_ADDR_LIMIT, 288);
        assert_eq!(S_STACKFRAME, 304);
        assert_eq!(S_FRAME_SIZE, 320);
    }
}

/// 虚拟地址 bit 数
pub const VA_BITS: usize = 48;

/// 物理地址 bit 数
pub const PA_BITS: usize = 48;

/// 内核起始虚拟地址
///
/// 最大到 2G
pub const KIMAGE_START: usize = 0xffff000000000000;

/// 内核 io 空间起始地址
///
/// 最大 16G
pub const KIO_VADDR: usize = 0xffff001000000000;
/// 内核 io 空间结束地址
pub const KIO_VADDR_END: usize = 0xffff001400000000;

/// 内核线性空间起始地址
///
/// 最大到 16G
pub const KMEM_VADDR: usize = 0xffff002000000000;
/// 内核线性空间结束地址
pub const KMEM_VADDR_END: usize = 0xffff002400000000;

/// 内核页结构空间起始地址
///
/// 最大到 16G
pub const KPAGE_VADDR: usize = 0xffff003000000000;
/// 内核页结构空间结束地址
pub const KPAGE_VADDR_END: usize = 0xffff003400000000;

/// el2 级别标识
pub const CurrentEL_EL2: usize = 2 << 2;

/// el1 系统寄存器预留1
pub const SCTLR_EL1_RES1: usize = 1 << 11 | 1 << 20 | 1 << 22 | 1 << 28 | 1 << 29;
/// el2 系统寄存器预留2
pub const SCTLR_EL2_RES1: usize =
    1 << 4 | 1 << 5 | 1 << 11 | 1 << 16 | 1 << 18 | 1 << 22 | 1 << 23 | 1 << 28 | 1 << 29;

/// el1 小端
pub const ENDIAN_SET_EL1: usize = 0;
/// el2 小端
pub const ENDIAN_SET_EL2: usize = 0;

/// boot 在 el1
pub const BOOT_CPU_MODE_EL1: usize = 0xe11;
/// boot 在 el2
pub const BOOT_CPU_MODE_EL2: usize = 0xe12;

/// SPSR bit F
pub const PSR_F_BIT: usize = 0x00000040;
/// SPSR bit I
pub const PSR_I_BIT: usize = 0x00000080;
/// SPSR bit A
pub const PSR_A_BIT: usize = 0x00000100;
/// SPSR bit D
pub const PSR_D_BIT: usize = 0x00000200;

/// psr el0t
pub const PSR_MODE_EL0t: usize = 0x00000000;
/// psr el1t
pub const PSR_MODE_EL1t: usize = 0x00000004;
/// psr el1h
pub const PSR_MODE_EL1h: usize = 0x00000005;
/// psr el2t
pub const PSR_MODE_EL2t: usize = 0x00000008;
/// psr el2h
pub const PSR_MODE_EL2h: usize = 0x00000009;
/// psr el3t
pub const PSR_MODE_EL3t: usize = 0x0000000c;
/// psr el3h
pub const PSR_MODE_EL3h: usize = 0x0000000d;
/// psr mask
pub const PSR_MODE_MASK: usize = 0x0000000f;

/// psr mode32
pub const PSR_MODE32_BIT: usize = 0x00000010;

/// nGnRnE
pub const MT_DEVICE_nGnRnE: usize = 0;
/// nGnRE
pub const MT_DEVICE_nGnRE: usize = 1;
/// GRE
pub const MT_DEVICE_GRE: usize = 2;
/// NC
pub const MT_NORMAL_NC: usize = 3;
/// NORMAL
pub const MT_NORMAL: usize = 4;
/// WT
pub const MT_NORMAL_WT: usize = 5;

/// 内存域属性
///
/// Memory region attributes for LPAE:
///
/// ```text
///  n = AttrIndx[2:0]
///      n   MAIR
///  DEVICE_nGnRnE   000    00000000
///  DEVICE_nGnRE    001    00000100
///  DEVICE_GRE      010    00001100
///  NORMAL_NC       011    01000100
///  NORMAL          100    11111111
///  NORMAL_WT       101    10111011
/// ```
pub const MAIR_ATTR: usize = (0x04 << (MT_DEVICE_nGnRE * 8))
    | (0x0c << (MT_DEVICE_GRE * 8))
    | (0x44 << (MT_NORMAL_NC * 8))
    | (0xff << (MT_NORMAL * 8))
    | (0xbb << (MT_NORMAL_WT * 8));

/// pgd 页表 type
pub const PGD_TYPE_TABLE: usize = 3;
/// pgd 页表特权级不可执行
pub const PGD_TABLE_PXN: usize = 1 << 59;
/// pgd 页表非特权级不可执行
pub const PGD_TABLE_UXN: usize = 1 << 60;

/// pud 页表 type
pub const PUD_TYPE_TABLE: usize = 3;
/// pud 页表特权级不可执行
pub const PUD_TABLE_PXN: usize = 1 << 59;
/// pud 页表非特权级不可执行
pub const PUD_TABLE_UXN: usize = 1 << 58;

/// pmd 页表 type
pub const PMD_TYPE_TABLE: usize = 3;
/// pmd 页表特权级不可执行
pub const PMD_TABLE_PXN: usize = 1 << 59;
/// pmd 页表非特权级不可执行
pub const PMD_TABLE_UXN: usize = 1 << 58;

/// pud 段
pub const PUD_TYPE_SECT: usize = 1 << 0;
/// pud af
pub const PUD_TYPE_AF: usize = 1 << 10;
/// pud
pub const PUD_SECT_S: usize = 3 << 8;
/// pud flags
pub const SWAPPER_PUD_FLAGS: usize = PUD_TYPE_SECT | PUD_TYPE_AF | PUD_SECT_S;

/// 默认的 mmu flags, 可读可写可执行
pub const SWAPPER_MM_NORMALFLAGS: usize = (MT_NORMAL << 2) | SWAPPER_PUD_FLAGS;
/// 默认的 mmu flags, NC IO 读写
pub const SWAPPER_MM_IOFLAGS: usize = (MT_DEVICE_nGnRE << 2) | SWAPPER_PUD_FLAGS;

/// pgd 偏移
pub const PGD_SHIFT: usize = 39;
/// pgd 大小 512G
pub const PGD_SIZE: usize = 0x8000000000;
/// pgd mask
pub const PGD_MASK: usize = 0xffffff8000000000;
/// pgd 数量
pub const PTRS_PER_PGD: usize = 512;

/// pud 偏移
pub const PUD_SHIFT: usize = 30;
/// pud 大小 1G
pub const PUD_SIZE: usize = 0x40000000;
/// pud mask
pub const PUD_MASK: usize = 0xffffffffc0000000;
/// pud 数量
pub const PTRS_PER_PUD: usize = 512;

/// pmd 偏移
pub const PMD_SHIFT: usize = 21;
/// pmd 大小 2M
pub const PMD_SIZE: usize = 0x200000;
/// pmd mask
pub const PMD_MASK: usize = 0xffffffffffe00000;
/// pmd 数量
pub const PTRS_PER_PMD: usize = 512;

/// pte 偏移
pub const PTE_SHIFT: usize = 12;
/// pte 大小 4K
pub const PTE_SIZE: usize = 0x1000;
/// pte mask
pub const PTE_MASK: usize = 0xfffffffffffff000;
/// pte 数量
pub const PTRS_PER_PTE: usize = 512;

/// 页大小
pub const PAGE_SIZE: usize = PTE_SIZE;

/// 线程栈大小(包括 irq 栈) 16K
pub const THREAD_STACK_SIZE: usize = 1 << 14;
/// `THREAD_STACK_SIZE` 的别名，对齐 Linux 内核 `<asm/thread_info.h>` 约定
pub const THREAD_SIZE: usize = THREAD_STACK_SIZE;

/// `SCTLR_ELx_DSSBS`
pub const SCTLR_ELx_DSSBS: usize = 1 << 44;
/// `SCTLR_ELx_ENIA`
pub const SCTLR_ELx_ENIA: usize = 1 << 31;
/// `SCTLR_ELx_ENIB`
pub const SCTLR_ELx_ENIB: usize = 1 << 30;
/// `SCTLR_ELx_ENDA`
pub const SCTLR_ELx_ENDA: usize = 1 << 27;
/// `SCTLR_ELx_EE`
pub const SCTLR_ELx_EE: usize = 1 << 25;
/// `SCTLR_ELx_IESB`
pub const SCTLR_ELx_IESB: usize = 1 << 21;
/// `SCTLR_ELx_WXN`
pub const SCTLR_ELx_WXN: usize = 1 << 19;
/// `SCTLR_ELx_ENDB`
pub const SCTLR_ELx_ENDB: usize = 1 << 13;
/// `SCTLR_ELx_I`
pub const SCTLR_ELx_I: usize = 1 << 12;
/// `SCTLR_ELx_SA`
pub const SCTLR_ELx_SA: usize = 1 << 3;
/// `SCTLR_ELx_C`
pub const SCTLR_ELx_C: usize = 1 << 2;
/// `SCTLR_ELx_A`
pub const SCTLR_ELx_A: usize = 1 << 1;
/// `SCTLR_ELx_M`
pub const SCTLR_ELx_M: usize = 1 << 0;

/// `SCTLR_EL1_UCI`
pub const SCTLR_EL1_UCI: usize = 1 << 26;
/// `SCTLR_EL1_E0E`
pub const SCTLR_EL1_E0E: usize = 1 << 24;
/// `SCTLR_EL1_SPAN`
pub const SCTLR_EL1_SPAN: usize = 1 << 23;
/// `SCTLR_EL1_NTWE`
pub const SCTLR_EL1_NTWE: usize = 1 << 18;
/// `SCTLR_EL1_NTWI`
pub const SCTLR_EL1_NTWI: usize = 1 << 16;
/// `SCTLR_EL1_UCT`
pub const SCTLR_EL1_UCT: usize = 1 << 15;
/// `SCTLR_EL1_DZE`
pub const SCTLR_EL1_DZE: usize = 1 << 14;
/// `SCTLR_EL1_UMA`
pub const SCTLR_EL1_UMA: usize = 1 << 9;
/// `SCTLR_EL1_SED`
pub const SCTLR_EL1_SED: usize = 1 << 8;
/// `SCTLR_EL1_ITD`
pub const SCTLR_EL1_ITD: usize = 1 << 7;
/// `SCTLR_EL1_CP15BEN`
pub const SCTLR_EL1_CP15BEN: usize = 1 << 5;
/// `SCTLR_EL1_SA0`
pub const SCTLR_EL1_SA0: usize = 1 << 4;

/// `SCTLR_EL1_SET`
pub const SCTLR_EL1_SET: usize = SCTLR_ELx_M
    | SCTLR_ELx_C
    | SCTLR_ELx_SA
    | SCTLR_EL1_SA0
    | SCTLR_EL1_SED
    | SCTLR_ELx_I
    | SCTLR_EL1_DZE
    | SCTLR_EL1_UCT
    | SCTLR_EL1_NTWE
    | SCTLR_ELx_IESB
    | SCTLR_EL1_SPAN
    | ENDIAN_SET_EL1
    | SCTLR_EL1_UCI
    | SCTLR_EL1_RES1;

/// `TCR_T0SZ_OFFSET`
pub const TCR_T0SZ_OFFSET: usize = 0;
/// `TCR_T1SZ_OFFSET`
pub const TCR_T1SZ_OFFSET: usize = 16;
/// `TCR_T0SZ`
pub const TCR_T0SZ: usize = (64 - VA_BITS) << TCR_T0SZ_OFFSET;
/// `TCR_T1SZ`
pub const TCR_T1SZ: usize = (64 - VA_BITS) << TCR_T1SZ_OFFSET;
/// `TCR_TxSZ`
pub const TCR_TxSZ: usize = TCR_T0SZ | TCR_T1SZ;
/// `TCR_TxSZ_WIDTH`
pub const TCR_TxSZ_WIDTH: usize = 6;

/// `TCR_IRGN0_SHIFT`
pub const TCR_IRGN0_SHIFT: usize = 8;
/// `TCR_IRGN1_SHIFT`
pub const TCR_IRGN1_SHIFT: usize = 24;
/// `TCR_IRGN0_WBWA`
pub const TCR_IRGN0_WBWA: usize = 1 << TCR_IRGN0_SHIFT;
/// `TCR_IRGN1_WBWA`
pub const TCR_IRGN1_WBWA: usize = 1 << TCR_IRGN1_SHIFT;
/// `TCR_IRGN_WBWA`
pub const TCR_IRGN_WBWA: usize = TCR_IRGN0_WBWA | TCR_IRGN1_WBWA;

/// `TCR_ORGN0_SHIFT`
pub const TCR_ORGN0_SHIFT: usize = 10;
/// `TCR_ORGN1_SHIFT`
pub const TCR_ORGN1_SHIFT: usize = 26;
/// `TCR_ORGN0_WBWA`
pub const TCR_ORGN0_WBWA: usize = 1 << TCR_ORGN0_SHIFT;
/// `TCR_ORGN1_WBWA`
pub const TCR_ORGN1_WBWA: usize = 1 << TCR_ORGN1_SHIFT;
/// `TCR_ORGN_WBWA`
pub const TCR_ORGN_WBWA: usize = TCR_ORGN0_WBWA | TCR_ORGN1_WBWA;

/// `TCR_SH0_SHIFT`
pub const TCR_SH0_SHIFT: usize = 12;
/// `TCR_SH1_SHIFT`
pub const TCR_SH1_SHIFT: usize = 28;
/// `TCR_SH0_INNER`
pub const TCR_SH0_INNER: usize = 3 << TCR_SH0_SHIFT;
/// `TCR_SH1_INNER`
pub const TCR_SH1_INNER: usize = 3 << TCR_SH1_SHIFT;
/// `TCR_SHARED`
pub const TCR_SHARED: usize = TCR_SH0_INNER | TCR_SH1_INNER;

/// `TCR_TG0_SHIFT`
pub const TCR_TG0_SHIFT: usize = 14;
/// `TCR_TG1_SHIFT`
pub const TCR_TG1_SHIFT: usize = 30;
/// `TCR_TG0_4K`
pub const TCR_TG0_4K: usize = 0 << TCR_TG0_SHIFT;
/// `TCR_TG1_4K`
pub const TCR_TG1_4K: usize = 2 << TCR_TG1_SHIFT;

/// `TCR_CACHE_FLAGS`
pub const TCR_CACHE_FLAGS: usize = TCR_IRGN_WBWA | TCR_ORGN_WBWA;
/// `TCR_SMP_FLAGS`
pub const TCR_SMP_FLAGS: usize = TCR_SHARED;
/// `TCR_TG_FLAGS`
pub const TCR_TG_FLAGS: usize = TCR_TG0_4K | TCR_TG1_4K;

/// `TCR_A1`
pub const TCR_A1: usize = 1 << 22;
/// `TCR_ASID16`
pub const TCR_ASID16: usize = 1 << 36;
/// `TCR_TBI0`
pub const TCR_TBI0: usize = 1 << 37;

/// `ID_AA64MMFR0_PARANGE_48`
pub const ID_AA64MMFR0_PARANGE_48: usize = 0x5;
/// `ID_AA64MMFR0_PARANGE_MAX`
pub const ID_AA64MMFR0_PARANGE_MAX: usize = ID_AA64MMFR0_PARANGE_48;
/// `ID_AA64MMFR0_PARANGE_SHIFT`
pub const ID_AA64MMFR0_PARANGE_SHIFT: usize = 0;
/// `TCR_IPS_SHIFT`
pub const TCR_IPS_SHIFT: usize = 32;

/// `ID_AA64MMFR0_TGRAN4_SHIFT`
pub const ID_AA64MMFR0_TGRAN4_SHIFT: usize = 28;
/// `ID_AA64MMFR0_TGRAN4_SUPPORTED`
pub const ID_AA64MMFR0_TGRAN4_SUPPORTED: usize = 0;
/// `ID_AA64MMFR0_TGRAN_SHIFT`
pub const ID_AA64MMFR0_TGRAN_SHIFT: usize = ID_AA64MMFR0_TGRAN4_SHIFT;
/// `ID_AA64MMFR0_TGRAN_SUPPORTED`
pub const ID_AA64MMFR0_TGRAN_SUPPORTED: usize = ID_AA64MMFR0_TGRAN4_SUPPORTED;

/// `KERNEL_DS`
pub const KERNEL_DS: usize = usize::MAX;
/// `USER_DS`
pub const USER_DS: usize = (1 << VA_BITS) - 1;

/// `NO_SYSCALL`
pub const NO_SYSCALL: isize = -1;

/// `BUG_BRK_IMM`
pub const BUG_BRK_IMM: usize = 0x800;

pub const ESR_ELx_EC_UNKNOWN: usize = 0x00;
pub const ESR_ELx_EC_WFx: usize = 0x01;
// Unallocated EC: 0x02
pub const ESR_ELx_EC_CP15_32: usize = 0x03;
pub const ESR_ELx_EC_CP15_64: usize = 0x04;
pub const ESR_ELx_EC_CP14_MR: usize = 0x05;
pub const ESR_ELx_EC_CP14_LS: usize = 0x06;
pub const ESR_ELx_EC_FP_ASIMD: usize = 0x07;
pub const ESR_ELx_EC_CP10_ID: usize = 0x08; // EL2 only
pub const ESR_ELx_EC_PAC: usize = 0x09; // EL2 and above
// Unallocated EC: 0x0A - 0x0B
pub const ESR_ELx_EC_CP14_64: usize = 0x0C;
// Unallocated EC: 0x0d
pub const ESR_ELx_EC_ILL: usize = 0x0E;
// Unallocated EC: 0x0F - 0x10
pub const ESR_ELx_EC_SVC32: usize = 0x11;
pub const ESR_ELx_EC_HVC32: usize = 0x12; // EL2 only
pub const ESR_ELx_EC_SMC32: usize = 0x13; // EL2 and above
// Unallocated EC: 0x14
pub const ESR_ELx_EC_SVC64: usize = 0x15;
pub const ESR_ELx_EC_HVC64: usize = 0x16; // EL2 and above
pub const ESR_ELx_EC_SMC64: usize = 0x17; // EL2 and above
pub const ESR_ELx_EC_SYS64: usize = 0x18;
pub const ESR_ELx_EC_SVE: usize = 0x19;
// Unallocated EC: 0x1A - 0x1E
pub const ESR_ELx_EC_IMP_DEF: usize = 0x1f; // EL3 only
pub const ESR_ELx_EC_IABT_LOW: usize = 0x20;
pub const ESR_ELx_EC_IABT_CUR: usize = 0x21;
pub const ESR_ELx_EC_PC_ALIGN: usize = 0x22;
// Unallocated EC: 0x23
pub const ESR_ELx_EC_DABT_LOW: usize = 0x24;
pub const ESR_ELx_EC_DABT_CUR: usize = 0x25;
pub const ESR_ELx_EC_SP_ALIGN: usize = 0x26;
// Unallocated EC: 0x27
pub const ESR_ELx_EC_FP_EXC32: usize = 0x28;
// Unallocated EC: 0x29 - 0x2B
pub const ESR_ELx_EC_FP_EXC64: usize = 0x2C;
// Unallocated EC: 0x2D - 0x2E
pub const ESR_ELx_EC_SERROR: usize = 0x2F;
pub const ESR_ELx_EC_BREAKPT_LOW: usize = 0x30;
pub const ESR_ELx_EC_BREAKPT_CUR: usize = 0x31;
pub const ESR_ELx_EC_SOFTSTP_LOW: usize = 0x32;
pub const ESR_ELx_EC_SOFTSTP_CUR: usize = 0x33;
pub const ESR_ELx_EC_WATCHPT_LOW: usize = 0x34;
pub const ESR_ELx_EC_WATCHPT_CUR: usize = 0x35;
// Unallocated EC: 0x36 - 0x37
pub const ESR_ELx_EC_BKPT32: usize = 0x38;
// Unallocated EC: 0x39
pub const ESR_ELx_EC_VECTOR32: usize = 0x3A; // EL2 only
// Unallocted EC: 0x3B
pub const ESR_ELx_EC_BRK64: usize = 0x3C;
// Unallocated EC: 0x3D - 0x3F
pub const ESR_ELx_EC_MAX: usize = 0x3F;

pub const ESR_ELx_EC_SHIFT: usize = 26;
pub const ESR_ELx_EC_MASK: usize = (0x3F) << ESR_ELx_EC_SHIFT;
pub const fn esr_elx_ec(esr: usize) -> usize {
    ((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT
}

pub const ESR_ELx_IL_SHIFT: usize = 25;
pub const ESR_ELx_IL: usize = (1) << ESR_ELx_IL_SHIFT;
pub const ESR_ELx_ISS_MASK: usize = ESR_ELx_IL - 1;

// ISS field definitions shared by different classes
pub const ESR_ELx_WNR_SHIFT: usize = 6;
pub const ESR_ELx_WNR: usize = (1) << ESR_ELx_WNR_SHIFT;

// Asynchronous Error Type
pub const ESR_ELx_IDS_SHIFT: usize = 24;
pub const ESR_ELx_IDS: usize = (1) << ESR_ELx_IDS_SHIFT;
pub const ESR_ELx_AET_SHIFT: usize = 10;
pub const ESR_ELx_AET: usize = (0x7) << ESR_ELx_AET_SHIFT;

pub const ESR_ELx_AET_UC: usize = (0) << ESR_ELx_AET_SHIFT;
pub const ESR_ELx_AET_UEU: usize = (1) << ESR_ELx_AET_SHIFT;
pub const ESR_ELx_AET_UEO: usize = (2) << ESR_ELx_AET_SHIFT;
pub const ESR_ELx_AET_UER: usize = (3) << ESR_ELx_AET_SHIFT;
pub const ESR_ELx_AET_CE: usize = (6) << ESR_ELx_AET_SHIFT;

// Shared ISS field definitions for Data/Instruction aborts
pub const ESR_ELx_SET_SHIFT: usize = 11;
pub const ESR_ELx_SET_MASK: usize = (3) << ESR_ELx_SET_SHIFT;
pub const ESR_ELx_FnV_SHIFT: usize = 10;
pub const ESR_ELx_FnV: usize = (1) << ESR_ELx_FnV_SHIFT;
pub const ESR_ELx_EA_SHIFT: usize = 9;
pub const ESR_ELx_EA: usize = (1) << ESR_ELx_EA_SHIFT;
pub const ESR_ELx_S1PTW_SHIFT: usize = 7;
pub const ESR_ELx_S1PTW: usize = (1) << ESR_ELx_S1PTW_SHIFT;

// Shared ISS fault status code(IFSC/DFSC) for Data/Instruction aborts
pub const ESR_ELx_FSC: usize = 0x3F;
pub const ESR_ELx_FSC_TYPE: usize = 0x3C;
pub const ESR_ELx_FSC_EXTABT: usize = 0x10;
pub const ESR_ELx_FSC_SERROR: usize = 0x11;
pub const ESR_ELx_FSC_ACCESS: usize = 0x08;
pub const ESR_ELx_FSC_FAT: usize = 0x04;
pub const ESR_ELx_FSC_PERM: usize = 0x0C;

// ISS field definitions for Data Aborts
pub const ESR_ELx_ISV_SHIFT: usize = 24;
pub const ESR_ELx_ISV: usize = (1) << ESR_ELx_ISV_SHIFT;
pub const ESR_ELx_SAS_SHIFT: usize = 22;
pub const ESR_ELx_SAS: usize = (3) << ESR_ELx_SAS_SHIFT;
pub const ESR_ELx_SSE_SHIFT: usize = 21;
pub const ESR_ELx_SSE: usize = (1) << ESR_ELx_SSE_SHIFT;
pub const ESR_ELx_SRT_SHIFT: usize = 16;
pub const ESR_ELx_SRT_MASK: usize = (0x1F) << ESR_ELx_SRT_SHIFT;
pub const ESR_ELx_SF_SHIFT: usize = 15;
pub const ESR_ELx_SF: usize = (1) << ESR_ELx_SF_SHIFT;
pub const ESR_ELx_AR_SHIFT: usize = 14;
pub const ESR_ELx_AR: usize = (1) << ESR_ELx_AR_SHIFT;
pub const ESR_ELx_CM_SHIFT: usize = 8;
pub const ESR_ELx_CM: usize = (1) << ESR_ELx_CM_SHIFT;

// ISS field definitions for exceptions taken in to Hyp
pub const ESR_ELx_CV: usize = (1) << 24;
pub const ESR_ELx_COND_SHIFT: usize = 20;
pub const ESR_ELx_COND_MASK: usize = (0xF) << ESR_ELx_COND_SHIFT;
pub const ESR_ELx_WFx_ISS_TI: usize = (1) << 0;
pub const ESR_ELx_WFx_ISS_WFI: usize = 0;
pub const ESR_ELx_WFx_ISS_WFE: usize = (1) << 0;
pub const ESR_ELx_xVC_IMM_MASK: usize = (1 << 16) - 1;

pub const DISR_EL1_IDS: usize = (1) << 24;
// DISR_EL1 and ESR_ELx share the bottom 13 bits, but the RES0 bits may mean
// different things in the future...
pub const DISR_EL1_ESR_MASK: usize = ESR_ELx_AET | ESR_ELx_EA | ESR_ELx_FSC;

// ESR value templates for specific events
pub const ESR_ELx_WFx_MASK: usize = ESR_ELx_EC_MASK | ESR_ELx_WFx_ISS_TI;
pub const ESR_ELx_WFx_WFI_VAL: usize = (ESR_ELx_EC_WFx << ESR_ELx_EC_SHIFT) | ESR_ELx_WFx_ISS_WFI;

pub const fn esr_elx_val_brk64(imm: usize) -> usize {
    (ESR_ELx_EC_BRK64 << ESR_ELx_EC_SHIFT) | ESR_ELx_IL | ((imm) & 0xffff)
}

// ISS field definitions for System instruction traps
pub const ESR_ELx_SYS64_ISS_RES0_SHIFT: usize = 22;
pub const ESR_ELx_SYS64_ISS_RES0_MASK: usize = (0x7) << ESR_ELx_SYS64_ISS_RES0_SHIFT;
pub const ESR_ELx_SYS64_ISS_DIR_MASK: usize = 0x1;
pub const ESR_ELx_SYS64_ISS_DIR_READ: usize = 0x1;
pub const ESR_ELx_SYS64_ISS_DIR_WRITE: usize = 0x0;

pub const ESR_ELx_SYS64_ISS_RT_SHIFT: usize = 5;
pub const ESR_ELx_SYS64_ISS_RT_MASK: usize = (0x1f) << ESR_ELx_SYS64_ISS_RT_SHIFT;
pub const ESR_ELx_SYS64_ISS_CRM_SHIFT: usize = 1;
pub const ESR_ELx_SYS64_ISS_CRM_MASK: usize = (0xf) << ESR_ELx_SYS64_ISS_CRM_SHIFT;
pub const ESR_ELx_SYS64_ISS_CRN_SHIFT: usize = 10;
pub const ESR_ELx_SYS64_ISS_CRN_MASK: usize = (0xf) << ESR_ELx_SYS64_ISS_CRN_SHIFT;
pub const ESR_ELx_SYS64_ISS_OP1_SHIFT: usize = 14;
pub const ESR_ELx_SYS64_ISS_OP1_MASK: usize = (0x7) << ESR_ELx_SYS64_ISS_OP1_SHIFT;
pub const ESR_ELx_SYS64_ISS_OP2_SHIFT: usize = 17;
pub const ESR_ELx_SYS64_ISS_OP2_MASK: usize = (0x7) << ESR_ELx_SYS64_ISS_OP2_SHIFT;
pub const ESR_ELx_SYS64_ISS_OP0_SHIFT: usize = 20;
pub const ESR_ELx_SYS64_ISS_OP0_MASK: usize = (0x3) << ESR_ELx_SYS64_ISS_OP0_SHIFT;
pub const ESR_ELx_SYS64_ISS_SYS_MASK: usize = ESR_ELx_SYS64_ISS_OP0_MASK
    | ESR_ELx_SYS64_ISS_OP1_MASK
    | ESR_ELx_SYS64_ISS_OP2_MASK
    | ESR_ELx_SYS64_ISS_CRN_MASK
    | ESR_ELx_SYS64_ISS_CRM_MASK;
pub const fn esr_elx_sys64_iss_sys_val(
    op0: usize,
    op1: usize,
    op2: usize,
    crn: usize,
    crm: usize,
) -> usize {
    ((op0) << ESR_ELx_SYS64_ISS_OP0_SHIFT)
        | ((op1) << ESR_ELx_SYS64_ISS_OP1_SHIFT)
        | ((op2) << ESR_ELx_SYS64_ISS_OP2_SHIFT)
        | ((crn) << ESR_ELx_SYS64_ISS_CRN_SHIFT)
        | ((crm) << ESR_ELx_SYS64_ISS_CRM_SHIFT)
}

pub const ESR_ELx_SYS64_ISS_SYS_OP_MASK: usize =
    ESR_ELx_SYS64_ISS_SYS_MASK | ESR_ELx_SYS64_ISS_DIR_MASK;
pub const fn esr_elx_sys64_iss_rt(esr: usize) -> usize {
    ((esr) & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT
}

// User space cache operations have the following sysreg encoding
// in System instructions.
// op0=1, op1=3, op2=1, crn=7, crm={ 5, 10, 11, 12, 14 }, WRITE (L=0)
pub const ESR_ELx_SYS64_ISS_CRM_DC_CIVAC: usize = 14;
pub const ESR_ELx_SYS64_ISS_CRM_DC_CVAP: usize = 12;
pub const ESR_ELx_SYS64_ISS_CRM_DC_CVAU: usize = 11;
pub const ESR_ELx_SYS64_ISS_CRM_DC_CVAC: usize = 10;
pub const ESR_ELx_SYS64_ISS_CRM_IC_IVAU: usize = 5;

pub const ESR_ELx_SYS64_ISS_EL0_CACHE_OP_MASK: usize = ESR_ELx_SYS64_ISS_OP0_MASK
    | ESR_ELx_SYS64_ISS_OP1_MASK
    | ESR_ELx_SYS64_ISS_OP2_MASK
    | ESR_ELx_SYS64_ISS_CRN_MASK
    | ESR_ELx_SYS64_ISS_DIR_MASK;
pub const ESR_ELx_SYS64_ISS_EL0_CACHE_OP_VAL: usize =
    esr_elx_sys64_iss_sys_val(1, 3, 1, 7, 0) | ESR_ELx_SYS64_ISS_DIR_WRITE;
// User space MRS operations which are supported for emulation
// have the following sysreg encoding in System instructions.
// op0 = 3, op1= 0, crn = 0, {crm = 0, 4-7}, READ (L = 1)
pub const ESR_ELx_SYS64_ISS_SYS_MRS_OP_MASK: usize = ESR_ELx_SYS64_ISS_OP0_MASK
    | ESR_ELx_SYS64_ISS_OP1_MASK
    | ESR_ELx_SYS64_ISS_CRN_MASK
    | ESR_ELx_SYS64_ISS_DIR_MASK;
pub const ESR_ELx_SYS64_ISS_SYS_MRS_OP_VAL: usize =
    esr_elx_sys64_iss_sys_val(3, 0, 0, 0, 0) | ESR_ELx_SYS64_ISS_DIR_READ;

pub const ESR_ELx_SYS64_ISS_SYS_CTR: usize = esr_elx_sys64_iss_sys_val(3, 3, 1, 0, 0);
pub const ESR_ELx_SYS64_ISS_SYS_CTR_READ: usize =
    ESR_ELx_SYS64_ISS_SYS_CTR | ESR_ELx_SYS64_ISS_DIR_READ;

pub const ESR_ELx_SYS64_ISS_SYS_CNTVCT: usize =
    esr_elx_sys64_iss_sys_val(3, 3, 2, 14, 0) | ESR_ELx_SYS64_ISS_DIR_READ;

pub const ESR_ELx_SYS64_ISS_SYS_CNTFRQ: usize =
    esr_elx_sys64_iss_sys_val(3, 3, 0, 14, 0) | ESR_ELx_SYS64_ISS_DIR_READ;

pub const fn esr_sys64_to_sysreg(e: usize) -> usize {
    sys_reg(
        ((e) & ESR_ELx_SYS64_ISS_OP0_MASK) >> ESR_ELx_SYS64_ISS_OP0_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_OP1_MASK) >> ESR_ELx_SYS64_ISS_OP1_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_CRN_MASK) >> ESR_ELx_SYS64_ISS_CRN_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> ESR_ELx_SYS64_ISS_OP2_SHIFT,
    )
}

pub const fn esr_cp15_to_sysreg(e: usize) -> usize {
    sys_reg(
        3,
        ((e) & ESR_ELx_SYS64_ISS_OP1_MASK) >> ESR_ELx_SYS64_ISS_OP1_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_CRN_MASK) >> ESR_ELx_SYS64_ISS_CRN_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT,
        ((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> ESR_ELx_SYS64_ISS_OP2_SHIFT,
    )
}

// ISS field definitions for floating-point exception traps
// (FP_EXC_32/FP_EXC_64).
//
// (The FPEXC_* constants are used instead for common bits.)

pub const ESR_ELx_FP_EXC_TFV: usize = (1) << 23;

// ISS field definitions for CP15 accesses
pub const ESR_ELx_CP15_32_ISS_DIR_MASK: usize = 0x1;
pub const ESR_ELx_CP15_32_ISS_DIR_READ: usize = 0x1;
pub const ESR_ELx_CP15_32_ISS_DIR_WRITE: usize = 0x0;

pub const ESR_ELx_CP15_32_ISS_RT_SHIFT: usize = 5;
pub const ESR_ELx_CP15_32_ISS_RT_MASK: usize = (0x1f) << ESR_ELx_CP15_32_ISS_RT_SHIFT;
pub const ESR_ELx_CP15_32_ISS_CRM_SHIFT: usize = 1;
pub const ESR_ELx_CP15_32_ISS_CRM_MASK: usize = (0xf) << ESR_ELx_CP15_32_ISS_CRM_SHIFT;
pub const ESR_ELx_CP15_32_ISS_CRN_SHIFT: usize = 10;
pub const ESR_ELx_CP15_32_ISS_CRN_MASK: usize = (0xf) << ESR_ELx_CP15_32_ISS_CRN_SHIFT;
pub const ESR_ELx_CP15_32_ISS_OP1_SHIFT: usize = 14;
pub const ESR_ELx_CP15_32_ISS_OP1_MASK: usize = (0x7) << ESR_ELx_CP15_32_ISS_OP1_SHIFT;
pub const ESR_ELx_CP15_32_ISS_OP2_SHIFT: usize = 17;
pub const ESR_ELx_CP15_32_ISS_OP2_MASK: usize = (0x7) << ESR_ELx_CP15_32_ISS_OP2_SHIFT;

pub const ESR_ELx_CP15_32_ISS_SYS_MASK: usize = ESR_ELx_CP15_32_ISS_OP1_MASK
    | ESR_ELx_CP15_32_ISS_OP2_MASK
    | ESR_ELx_CP15_32_ISS_CRN_MASK
    | ESR_ELx_CP15_32_ISS_CRM_MASK
    | ESR_ELx_CP15_32_ISS_DIR_MASK;
pub const fn esr_elx_cp15_32_iss_sys_val(op1: usize, op2: usize, crn: usize, crm: usize) -> usize {
    ((op1) << ESR_ELx_CP15_32_ISS_OP1_SHIFT)
        | ((op2) << ESR_ELx_CP15_32_ISS_OP2_SHIFT)
        | ((crn) << ESR_ELx_CP15_32_ISS_CRN_SHIFT)
        | ((crm) << ESR_ELx_CP15_32_ISS_CRM_SHIFT)
}

pub const ESR_ELx_CP15_64_ISS_DIR_MASK: usize = 0x1;
pub const ESR_ELx_CP15_64_ISS_DIR_READ: usize = 0x1;
pub const ESR_ELx_CP15_64_ISS_DIR_WRITE: usize = 0x0;

pub const ESR_ELx_CP15_64_ISS_RT_SHIFT: usize = 5;
pub const ESR_ELx_CP15_64_ISS_RT_MASK: usize = (0x1f) << ESR_ELx_CP15_64_ISS_RT_SHIFT;

pub const ESR_ELx_CP15_64_ISS_RT2_SHIFT: usize = 10;
pub const ESR_ELx_CP15_64_ISS_RT2_MASK: usize = (0x1f) << ESR_ELx_CP15_64_ISS_RT2_SHIFT;

pub const ESR_ELx_CP15_64_ISS_OP1_SHIFT: usize = 16;
pub const ESR_ELx_CP15_64_ISS_OP1_MASK: usize = (0xf) << ESR_ELx_CP15_64_ISS_OP1_SHIFT;
pub const ESR_ELx_CP15_64_ISS_CRM_SHIFT: usize = 1;
pub const ESR_ELx_CP15_64_ISS_CRM_MASK: usize = (0xf) << ESR_ELx_CP15_64_ISS_CRM_SHIFT;

pub const fn esr_elx_cp15_64_iss_sys_val(op1: usize, crm: usize) -> usize {
    ((op1) << ESR_ELx_CP15_64_ISS_OP1_SHIFT) | ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT)
}

pub const ESR_ELx_CP15_64_ISS_SYS_MASK: usize =
    ESR_ELx_CP15_64_ISS_OP1_MASK | ESR_ELx_CP15_64_ISS_CRM_MASK | ESR_ELx_CP15_64_ISS_DIR_MASK;

pub const ESR_ELx_CP15_64_ISS_SYS_CNTVCT: usize =
    esr_elx_cp15_64_iss_sys_val(1, 14) | ESR_ELx_CP15_64_ISS_DIR_READ;

pub const ESR_ELx_CP15_32_ISS_SYS_CNTFRQ: usize =
    esr_elx_cp15_32_iss_sys_val(0, 0, 14, 0) | ESR_ELx_CP15_32_ISS_DIR_READ;

pub const fn esr_is_data_abort(esr: usize) -> bool {
    let ec = esr_elx_ec(esr);
    ec == ESR_ELx_EC_DABT_LOW || ec == ESR_ELx_EC_DABT_CUR
}

// ARMv8 ARM reserves the following encoding for system registers:
// (Ref: ARMv8 ARM, Section: "System instruction class encoding overview",
//  C5.2, version:ARM DDI 0487A.f)
// 	[20-19] : Op0
// 	[18-16] : Op1
// 	[15-12] : CRn
// 	[11-8]  : CRm
// 	[7-5]   : Op2
pub const Op0_shift: usize = 19;
pub const Op0_mask: usize = 0x3;
pub const Op1_shift: usize = 16;
pub const Op1_mask: usize = 0x7;
pub const CRn_shift: usize = 12;
pub const CRn_mask: usize = 0xf;
pub const CRm_shift: usize = 8;
pub const CRm_mask: usize = 0xf;
pub const Op2_shift: usize = 5;
pub const Op2_mask: usize = 0x7;

pub const fn sys_reg(op0: usize, op1: usize, crn: usize, crm: usize, op2: usize) -> usize {
    ((op0) << Op0_shift)
        | ((op1) << Op1_shift)
        | ((crn) << CRn_shift)
        | ((crm) << CRm_shift)
        | ((op2) << Op2_shift)
}

/// `TIF_NEED_RESCHED`
pub const TIF_NEED_RESCHED: usize = 1;

/// `_TIF_WORK_MASK`
pub const _TIF_WORK_MASK: usize = 1 << TIF_NEED_RESCHED;
