//
//  astris_armcpu.h
//  Astris
//
//  Created by Andrew Myrick on 9/22/11.
//  Copyright (c) 2011-2012 Apple Inc. All rights reserved.
//

#ifndef _ASTRIS_ARMCPU_H
#define _ASTRIS_ARMCPU_H

#include "astris_cpu.h"
#include "astris_arm.h"

// Set the default byte size for most efficient ARM CPU memory transfer chunking.
// Specific derivates can support a different memory quantum size which can be
// determined using the _memory_quantum method.
#define ARMCPU_MEMORY_QUANTUM_DEFAULT 4

typedef uint32_t AXinstr_t;

//
// ARMdapcpu_t -- minimal class for DAP-based CPU that isn't present yet;
//                can't inhereit anything useful from ARMcpu_t, so don't bother.
//
class ARMdapcpu_t:
    public CPU_t {
public:
    ARMdapcpu_t(AXtgt_t *tgt, const char *name);

protected:
    virtual int _status_hw(uint32_t *status, uint32_t *flags);

private:
    AXap_t *_memap;
};

//
// ARMcpu_t -- abstract class for v6 and later ARM CPUs
//
class ARMcpu_t:
    public CPU_t {
public:
    ARMcpu_t(AXtgt_t *tgt, const char *name, uint32_t id);
    virtual ~ARMcpu_t();

    virtual int memory_read(AXmemaddr_t address, uint32_t *length, uint8_t *buffer);
    virtual int memory_write(AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int iflush);
    virtual int memory_write(AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int iflush, int width);

    virtual int cache_op(cpu_cacheop_t op, AXmemaddr_t addr, uint32_t length);

    //
    // Architectural extension detection
    //
    virtual bool has_virtualization() {return false;}
    virtual bool has_large_phys()     {return false;}
    virtual bool has_security()       {return false;}
    virtual bool has_multiproc()      {return false;}
    virtual bool has_vmsa_support()   {return true;}

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

    //
    // Breakpoints - common to ARM32 and ARM64
    //
    virtual int _setup_bp(bpwp_info_t *info, int *bp_state);
    virtual int _setup_wp(bpwp_info_t *info, int *wp_state);
    virtual int _set_bpwp(int op, bpwp_info_t *info);
    virtual int _insert_breakpoint(bpwp_info_t *info, AXmemaddr_t address);
    virtual int _remove_breakpoint(int which_bp);
    virtual int _insert_watchpoint(bpwp_info_t *info, AXmemaddr_t address, uint32_t len, int rdwr);
    virtual int _remove_watchpoint(int which_wp);

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

    //
    // Memory access helpers - filled in by subclasses
    //
    virtual int  _memory_quantum() {return ARMCPU_MEMORY_QUANTUM_DEFAULT;}
    virtual void _memory_clip(AXmemaddr_t address, uint32_t *length)
    {
        if (address > (AXmemaddr_t)UINT32_MAX) {
            *length = 0;

            // An address of 0x0 will overflow and cause us to incorrectly adjust
            // length.  We'll catch that case here.
        } else if ((address > 0x0) && (((AXmemaddr_t)UINT32_MAX - address) + 1 < *length)) {
            *length = (uint32_t)(((AXmemaddr_t)UINT32_MAX) - address) + 1;
        }
    }

    virtual bool _memory_fastmode(AXmemaddr_t address, uint32_t length) {return false;}
    virtual int  _memory_set_base(AXmemaddr_t address)                                                 = 0;
    virtual int  _read_memory_slow(AXmemaddr_t address, uint32_t *length, uint8_t *buffer)             = 0;
    virtual int  _read_memory_fast(AXmemaddr_t address, uint32_t *length, uint8_t *buffer)             = 0;
    virtual int  _write_memory_slow(AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int width) = 0;
    virtual int  _write_memory_fast(AXmemaddr_t address, uint32_t *length, uint8_t *buffer)            = 0;

    //
    // Cache operations
    //
    virtual int _cache_op_l1(cpu_cacheop_t op, AXmemaddr_t addr, uint32_t length);
    virtual int _cache_op_l2(cpu_cacheop_t op, AXmemaddr_t addr, uint32_t length);

    virtual int _clean_dcache_setway(uint32_t set, uint32_t way)             {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _clean_dcache_vaddr(AXmemaddr_t addr)                        {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _clean_invalidate_dcache_setway(uint32_t set, uint32_t way)  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _clean_invalidate_dcache_vaddr(AXmemaddr_t addr)             {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _invalidate_icache_all()                                     {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _invalidate_icache_vaddr(AXmemaddr_t addr)                   {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _clean_l2cache_setway(uint32_t set, uint32_t way)            {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _clean_invalidate_l2cache_setway(uint32_t set, uint32_t way) {return ASTRIS_ERR_NOTSUPPORTED;}

    //
    // Low-level breakpoint/watchpoint operations
    //
    virtual int _get_bpcr_hw(uint32_t index, uint32_t *bpcr)        {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _get_wpcr_hw(uint32_t index, uint32_t *wpcr)        {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _set_bpcr_hw(uint32_t index, uint32_t bpcr)         {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _set_wpcr_hw(uint32_t index, uint32_t wpcr)         {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _get_bpaddr_hw(uint32_t index, AXmemaddr_t *bpaddr) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _get_wpaddr_hw(uint32_t index, AXmemaddr_t *wpaddr) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _set_bpaddr_hw(uint32_t index, AXmemaddr_t bpaddr)  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _set_wpaddr_hw(uint32_t index, AXmemaddr_t wpaddr)  {return ASTRIS_ERR_NOTSUPPORTED;}

    //
    // Misc.
    //
    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 _set_mmreg_bit(AXregaddr_t regaddr, unsigned int bit, bool asserted, bool force = false, bool confirm = false);

    virtual int  _exec(AXinstr_t instr) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _check_abort()         {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _synchronize_context() {return ASTRIS_ERR_OK;}
    virtual int  _sstep()               {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual void _init_params()         {return;}

    int m_dc_lineshift; // Data-cache size coefficients
    int m_dc_linebytes;
    int m_dc_wayshift;
    int m_dc_ways;
    int m_dc_sets;

    int m_ic_lineshift; // Instruction-cache size coefficients
    int m_ic_linebytes;
    int m_ic_wayshift;
    int m_ic_ways;
    int m_ic_sets;

    int m_l2_lineshift; // L2 cache size coefficients (when L2 controlled via coprocessor registers)
    int m_l2_linebytes;
    int m_l2_wayshift;
    int m_l2_ways;
    int m_l2_sets;

    int m_flags;

    AXap_t *_memap;
    AXap_t *_memap_sys;
    uint32_t _id;
};

#endif // ifndef _ASTRIS_ARMCPU_H
