//
//  astris_arm32cpu.h
//  Astris
//
//  Created by Kip Walker on 1/7/12.
//  Copyright (c) 2012 Apple Inc. All rights reserved.
//

#ifndef Astris_astris_arm32cpu_h
#define Astris_astris_arm32cpu_h

#include "astris_armcpu.h"

///////////////////////////////////////////////////////////////////////////////
// INTERNAL ERROR BRANCHING CONTROL
//
// Several locations have been identified where errors occurring within a
// function are ignored. This can lead to further corruption of communication
// with the device.
//
// To address this problem, a staged approach has been taken.
//   Stage 1:
//     CPU32_CONSIDER_CODE macro used to log (spew level) but not alter flow
//   Stage 2: [performed on a function-by-function basis (as needed)]
//     Decide whether error code should cause function to alter flow:
//       alters flow: replace CPU32_CONSIDER_CODE with CPU[SPEW]_REQUIRE_CODE
//       no flow change: replace CPU32_CONSIDER_CODE with CPU[SPEW]_VERIFY_CODE
//                       (removing label since not applicable to VERIFY)
///////////////////////////////////////////////////////////////////////////////

#define CPU32_CONSIDER_CODE(result, label, fmt, ...) \
    CPU_VERIFY_CODE(result, fmt, ## __VA_ARGS__)

#define CPU32SPEW_CONSIDER_CODE(result, label, fmt, ...) \
    CPUSPEW_VERIFY_CODE(result, fmt, ## __VA_ARGS__)

#define CPU32_REQUIRE_LOG1(check, label, first_id, fmt, ...)  \
    if (check) {                                              \
        _logged_first[ARM32CPU_LOG1_ ## first_id] = false;    \
    } else {                                                  \
        if (!(_logged_first[ARM32CPU_LOG1_ ## first_id])) {   \
            AXlog_err(_log_key, fmt, ## __VA_ARGS__);         \
            _logged_first[ARM32CPU_LOG1_ ## first_id] = true; \
        }                                                     \
        goto label;                                           \
    }

class ARM32cpu_t:
    public ARMcpu_t {
public:
    ARM32cpu_t(AXtgt_t *tgt, const char *name, uint32_t id);
    virtual ~ARM32cpu_t();

    virtual int cpu_mode(const char **mode, uint32_t cpsr);

    virtual int read_register(AXregid_t reg, AXregval_t *val);
    virtual int write_register(AXregid_t reg, AXregval_t val);

    virtual CPUFamily get_cpu_family() {return CPUFamily::kARM32;}

protected:
    const char *_log_key; // ARM32 CPU logging domain

    virtual bool _init_hw();
    virtual bool _cleanup_hw();
    virtual int  _status_hw(uint32_t *status, uint32_t *flags);
    virtual int  _collect_hw(int timeout);
    virtual int  _flush_state_hw();
    virtual int  _is_halted(bool *halted);

    //
    // ARMcpu_t breakpoints override (to support single-step)
    //
    virtual int _setup_bp(bpwp_info_t *info, int *bp_state);
    virtual int _software_breakpoint(uint32_t *len, uint32_t *inst);

    //
    // Memory access helper overrides
    //
    virtual bool _memory_fastmode(AXmemaddr_t address, uint32_t length);
    virtual int  _memory_set_base(AXmemaddr_t address);
    virtual int  _read_memory_slow(AXmemaddr_t address, uint32_t *length, uint8_t *buffer);
    virtual int  _read_memory_fast(AXmemaddr_t address, uint32_t *length, uint8_t *buffer);
    virtual int  _write_memory_slow(AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int width);
    virtual int  _write_memory_fast(AXmemaddr_t address, uint32_t *length, uint8_t *buffer);

    //
    // Cache operations
    //
    virtual int _clean_dcache_setway(uint32_t set, uint32_t way);
    virtual int _clean_dcache_vaddr(AXmemaddr_t addr);
    virtual int _clean_invalidate_dcache_setway(uint32_t set, uint32_t way);
    virtual int _clean_invalidate_dcache_vaddr(AXmemaddr_t addr);
    virtual int _invalidate_icache_all();
    virtual int _invalidate_icache_vaddr(AXmemaddr_t addr);
    virtual int _clean_l2cache_setway(uint32_t set, uint32_t way);

    //
    // Low-level breakpoint/watchpoint operations
    //
    virtual int _get_num_hwbp_hw(int *numbp);
    virtual int _get_num_hwwp_hw(int *numwp);
    virtual int _get_bpcr_hw(uint32_t index, uint32_t *bpcr);
    virtual int _get_wpcr_hw(uint32_t index, uint32_t *wpcr);
    virtual int _set_bpcr_hw(uint32_t index, uint32_t bpcr);
    virtual int _set_wpcr_hw(uint32_t index, uint32_t wpcr);
    virtual int _get_bpaddr_hw(uint32_t index, AXmemaddr_t *bpaddr);
    virtual int _get_wpaddr_hw(uint32_t index, AXmemaddr_t *wpaddr);
    virtual int _set_bpaddr_hw(uint32_t index, AXmemaddr_t bpaddr);
    virtual int _set_wpaddr_hw(uint32_t index, AXmemaddr_t wpaddr);

    //
    // Address translation control implementation
    //
    virtual int _suspend_translation_hw(void);
    virtual int _resume_translation_hw(void);

    //
    // Memory access control implementation
    //
    virtual int _suspend_permission_checks(void);
    virtual int _resume_permission_checks(void);

    //
    // Misc.
    //
    virtual int _exec(AXinstr_t instr) = 0;
    virtual int _check_abort();

    virtual int _synchronize_context() {return 0;}
    virtual int _sstep();

    //
    // Methods below are specific to ARM32cpu_t and its subclasses
    //

    virtual int _set_halting(bool enable);
    virtual int _set_corenpdnrq(bool enable);
    virtual int _set_force_writethrough(bool enable);

    virtual bool supports_dtrmode() {return true;}
    virtual int  get_dtrmode()      {return m_dtrmode;}
    virtual int  set_dtrmode(int dtrmode);

    virtual int  _rw_preflight(AXregaddr_t regaddr);
    virtual void _init_params();

    // Register manipulation
    virtual int _read_register32(AXregid_t reg, uint32_t *val);
    virtual int _read_register64(AXregid_t reg, uint64_t *val);
    virtual int _update_register32(AXregid_t reg, uint32_t val, int commit);
    virtual int _update_register64(AXregid_t reg, uint64_t val);
    virtual int cop_move(coproc_t cpreg, int gpr, bool is_mcr);
    virtual int get_gpr(int gpr, uint32_t &val);
    virtual int set_gpr(int gpr, uint32_t val);

    static int      regset_from_cpsr(uint32_t cpsr);
    static uint32_t cpsr_from_regset(int regset);
    virtual int     switch_regset(int regset, uint32_t *oldcpsr);
    virtual int     unswitch_regset(uint32_t oldcpsr);

    virtual int _read_gpr(int regset, int reg, uint32_t *val);
    virtual int _write_gpr(int regset, int reg, uint32_t val, int commit);
    virtual int _read_spsr(int regset, uint32_t &val);
    virtual int _write_spsr(int regset, uint32_t val);

    virtual void invalidate_all();

    // Run control and status
    virtual int check_dcc();

    // Collect helpers
    virtual int handle_pending_async_aborts(uint32_t &dpreg) {return ASTRIS_ERR_OK;}

    uint32_t m_cpsr;

    static const int MAX_DCC_VALS = 32;

    int m_dcc_words;
    int m_dcc_vals[MAX_DCC_VALS];

private:
    static uint32_t m_arm_statemap[];

    static const int REGMAP_EXTRA_COUNT = 15;

    cpureg_t *m_registers[REGSET_MAX][REGSET_INDEX_COUNT];
    cpureg_t m_regdata[REGSET_INDEX_COUNT + REGMAP_EXTRA_COUNT];
    cpureg_t m_spsr[REGSET_MAX];

    int m_dtrmode;
    int m_regset;
    AXregval_t m_par;
    uint32_t m_dfsr;
    uint32_t m_adfsr;
    uint32_t m_dfar;
    uint32_t m_dacr; // last read Domain Access Control Register [VMSA]

    bpwp_info_t *m_step_bp;

    typedef enum {
        ARM32CPU_LOG1_LSR_LOCK   = 0,
        ARM32CPU_LOG1_OSLSR_LOCK = 1,
        ARM32CPU_LOG1_OSLK_LOCK  = 2,
        ARM32CPU_LOG1_COUNT      = 3
    } ARM32CPU_LOG1_t;

    bool _logged_first[ARM32CPU_LOG1_COUNT]; // set = first fail log issued
};

class ARMv6cpu_t:
    public ARM32cpu_t {
public:
    ARMv6cpu_t(AXtgt_t *tgt, const char *name);

    // High-level interfaces with ARMv6-specific implementation
    virtual int read_dcc(int *got_val, uint32_t *val);

protected:
    const char *_log_key; // ARMv6 CPU logging domain

    virtual int _get_type_hw() {return AXCPUTYPE_ARM11;}
    virtual int _get_arch_hw() {return AXCPUARCH_ARMV6;}

    virtual int _halt_hw();
    virtual int _resume_hw(bool step);

    virtual int _read_dap(AXregaddr_t regaddr, uint32_t *regval, int nwords);
    virtual int _write_dap(AXregaddr_t regaddr, uint32_t *regval, int nwords);
    virtual int _exec(AXinstr_t instr);

    virtual bool supports_dtrmode()                           {return false;}
    virtual int  handle_pending_async_aborts(uint32_t &dpreg) {return ASTRIS_ERR_OK;}

    // Silently send these to /dev/null
    virtual int _set_corenpdnrq(bool enable)         {return ASTRIS_ERR_OK;}
    virtual int _set_force_writethrough(bool enable) {return ASTRIS_ERR_OK;}

private:
    /* Debug TAP instructions */
    enum {
        I_DBGTAP_EXTEST  = 0x00, //  0, bit-reversed
        I_DBGTAP_SCAN_N  = 0x40, //  2,
        I_DBGTAP_RESTART = 0x20, //  4,
        I_DBGTAP_HALT    = 0x10, //  8,
        I_DBGTAP_INTEST  = 0x30, // 12,
        I_DBGTAP_ITRSEL  = 0xb8, // 29,
        I_DBGTAP_IDCODE  = 0x78, // 30,
        I_DBGTAP_BYPASS  = 0xf8, // 31,
    };

    /* SCAN_N register selector values */
    enum {
        SC_DBGTAP_DIDR = 0x00, //  0, bit-reversed
        SC_DBGTAP_DSCR = 0x80, //  1,
        SC_DBGTAP_ITR  = 0x20, //  4,
        SC_DBGTAP_DTR  = 0xa0, //  5,
        SC_DBGTAP_ETM  = 0x60, //  6,
        SC_DBGTAP_REGS = 0xe0, //  7
    };

    /* Temporary... this stuff will move into the server */
    static const uint8_t _nib[];
    static inline uint8_t  _byterev(uint8_t x) {return _nib[(x >> 4)] | (_nib[x & 0xf] << 4);}
    static inline uint32_t _wordrev(uint32_t x)
    {
        return (_byterev(x & 0xff) << 24) |
               (_byterev((x >> 8) & 0xff) << 16) |
               (_byterev((x >> 16) & 0xff) << 8) |
               (_byterev((x >> 24) & 0xff));
    }

    int _read_internal(uint32_t regaddr, uint32_t *regval);
    int _write_internal(uint32_t regaddr, uint32_t regval);

};

class ARMv7cpu_t:
    public ARM32cpu_t {
public:
    ARMv7cpu_t(AXtgt_t *tgt, const char *name, uint32_t id);

    virtual int read_dcc(int *got_val, uint32_t *val);

    virtual int translate(AXmemaddr_t va, AXmemaddr_t *pa, bool write, int *result);

    //
    // Architectural extension detection
    //
    virtual bool has_virtualization() {return _virtualization;}
    virtual bool has_large_phys()     {return _large_phys;}
    virtual bool has_security()       {return _security;}
    virtual bool has_multiproc()      {return _multiproc;}
    virtual bool has_vmsa_support()   {return _vmsa_support;}
    virtual int  get_sw_info(AXrtxcid_t *sw_info);

protected:
    const char *_log_key; // ARMv7 CPU logging domain

    virtual int _get_arch_hw() {return AXCPUARCH_ARMV7;}

    virtual int  _get_type_hw();
    virtual bool _init_hw();
    virtual int  _halt_hw();
    virtual int  _resume_hw(bool step);
    virtual int  _read_dap(AXregaddr_t regaddr, uint32_t *regval, int nwords);
    virtual int  _write_dap(AXregaddr_t regaddr, uint32_t *regval, int nwords);
    virtual int  _exec(AXinstr_t instr);

    virtual int handle_pending_async_aborts(uint32_t &dpreg);

private:
    bool _virtualization;
    bool _large_phys;
    bool _security;
    bool _multiproc;
    bool _vmsa_support;
};

#endif // ifndef Astris_astris_arm32cpu_h
