qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v4 29/32] target-xtensa: implement memory protec


From: Blue Swirl
Subject: Re: [Qemu-devel] [PATCH v4 29/32] target-xtensa: implement memory protection options
Date: Sun, 4 Sep 2011 18:32:16 +0000

On Thu, Sep 1, 2011 at 8:45 PM, Max Filippov <address@hidden> wrote:
> - TLB opcode group;
> - region protection option (ISA, 4.6.3);
> - region translation option (ISA, 4.6.4);
> - MMU option (ISA, 4.6.5).
>
> Cache control attribute bits are not used by this implementation.
>
> Signed-off-by: Max Filippov <address@hidden>
> ---
>  target-xtensa/cpu.h       |   56 ++++++++-
>  target-xtensa/helper.c    |  340 
> ++++++++++++++++++++++++++++++++++++++++++++-
>  target-xtensa/helpers.h   |    7 +
>  target-xtensa/op_helper.c |  301 +++++++++++++++++++++++++++++++++++++++-
>  target-xtensa/translate.c |   91 ++++++++++++-
>  5 files changed, 782 insertions(+), 13 deletions(-)
>
> diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
> index 93e17d1..05948f1 100644
> --- a/target-xtensa/cpu.h
> +++ b/target-xtensa/cpu.h
> @@ -114,6 +114,10 @@ enum {
>     SCOMPARE1 = 12,
>     WINDOW_BASE = 72,
>     WINDOW_START = 73,
> +    PTEVADDR = 83,
> +    RASID = 90,
> +    ITLBCFG = 91,
> +    DTLBCFG = 92,
>     EPC1 = 177,
>     DEPC = 192,
>     EPS2 = 194,
> @@ -154,6 +158,9 @@ enum {
>  #define MAX_NLEVEL 6
>  #define MAX_NNMI 1
>  #define MAX_NCCOMPARE 3
> +#define MAX_TLB_WAY_SIZE 8
> +
> +#define REGION_PAGE_MASK 0xe0000000
>
>  enum {
>     /* Static vectors */
> @@ -214,6 +221,21 @@ typedef enum {
>     INTTYPE_MAX
>  } interrupt_type;
>
> +typedef struct xtensa_tlb_entry {
> +    uint32_t vaddr;
> +    uint32_t paddr;
> +    uint8_t asid;
> +    uint8_t attr;
> +    bool variable;
> +} xtensa_tlb_entry;
> +
> +typedef struct xtensa_tlb {
> +    unsigned nways;
> +    const unsigned way_size[10];
> +    bool varway56;
> +    unsigned nrefillentries;
> +} xtensa_tlb;
> +
>  typedef struct XtensaGdbReg {
>     int targno;
>     int type;
> @@ -248,6 +270,9 @@ typedef struct XtensaConfig {
>     unsigned nccompare;
>     uint32_t timerint[MAX_NCCOMPARE];
>     uint32_t clock_freq_khz;
> +
> +    xtensa_tlb itlb;
> +    xtensa_tlb dtlb;
>  } XtensaConfig;
>
>  typedef struct CPUXtensaState {
> @@ -258,6 +283,10 @@ typedef struct CPUXtensaState {
>     uint32_t uregs[256];
>     uint32_t phys_regs[MAX_NAREG];
>
> +    xtensa_tlb_entry itlb[7][MAX_TLB_WAY_SIZE];
> +    xtensa_tlb_entry dtlb[10][MAX_TLB_WAY_SIZE];
> +    unsigned autorefill_idx;
> +
>     int pending_irq_level; /* level of last raised IRQ */
>     void **irq_inputs;
>     QEMUTimer *ccompare_timer;
> @@ -287,12 +316,29 @@ int cpu_xtensa_signal_handler(int host_signum, void 
> *pinfo, void *puc);
>  void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
>  void xtensa_sync_window_from_phys(CPUState *env);
>  void xtensa_sync_phys_from_window(CPUState *env);
> +uint32_t xtensa_tlb_get_addr_mask(const CPUState *env, bool dtlb, uint32_t 
> way);
> +void split_tlb_entry_spec_way(const CPUState *env, uint32_t v, bool dtlb,
> +        uint32_t *vpn, uint32_t wi, uint32_t *ei);
> +int xtensa_tlb_lookup(const CPUState *env, uint32_t addr, bool dtlb,
> +        uint32_t *_wi, uint32_t *_ei, uint8_t *_ring);
> +void xtensa_tlb_set_entry(CPUState *env, bool dtlb,
> +        unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte);
> +int xtensa_get_physical_addr(CPUState *env,
> +        uint32_t vaddr, int is_write, int mmu_idx,
> +        uint32_t *paddr, uint32_t *page_size, unsigned *access);
> +
>
>  #define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt))
>
> +static inline bool xtensa_option_bits_enabled(const XtensaConfig *config,
> +        uint64_t opt)
> +{
> +    return (config->options & opt) != 0;
> +}
> +
>  static inline bool xtensa_option_enabled(const XtensaConfig *config, int opt)
>  {
> -    return (config->options & XTENSA_OPTION_BIT(opt)) != 0;
> +    return xtensa_option_bits_enabled(config, XTENSA_OPTION_BIT(opt));
>  }
>
>  static inline int xtensa_get_cintlevel(const CPUState *env)
> @@ -323,6 +369,14 @@ static inline int xtensa_get_cring(const CPUState *env)
>     }
>  }
>
> +static inline xtensa_tlb_entry *xtensa_tlb_get_entry(CPUState *env,
> +        bool dtlb, unsigned wi, unsigned ei)
> +{
> +    return dtlb ?
> +        env->dtlb[wi] + ei :
> +        env->itlb[wi] + ei;
> +}
> +
>  /* MMU modes definitions */
>  #define MMU_MODE0_SUFFIX _ring0
>  #define MMU_MODE1_SUFFIX _ring1
> diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c
> index 487847c..00571e8 100644
> --- a/target-xtensa/helper.c
> +++ b/target-xtensa/helper.c
> @@ -38,6 +38,8 @@
>         a1, a2, a3, a4, a5, a6) \
>     { .targno = (_targno), .type = (typ), .group = (_group) },
>
> +static void reset_mmu(CPUState *env);
> +
>  void cpu_reset(CPUXtensaState *env)
>  {
>     env->exception_taken = 0;
> @@ -48,6 +50,7 @@ void cpu_reset(CPUXtensaState *env)
>     env->sregs[VECBASE] = env->config->vecbase;
>
>     env->pending_irq_level = 0;
> +    reset_mmu(env);
>  }
>
>  static const XtensaConfig core_config[] = {
> @@ -150,7 +153,19 @@ void xtensa_cpu_list(FILE *f, fprintf_function 
> cpu_fprintf)
>
>  target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
>  {
> -    return addr;
> +    uint32_t paddr;
> +    uint32_t page_size;
> +    unsigned access;
> +
> +    if (0 == xtensa_get_physical_addr(env, addr, 0, 0,
> +                &paddr, &page_size, &access)) {

This order in comparison looks alien.

> +        return paddr;
> +    }
> +    if (0 == xtensa_get_physical_addr(env, addr, 2, 0,
> +                &paddr, &page_size, &access)) {
> +        return paddr;
> +    }
> +    return ~0;
>  }
>
>  static uint32_t relocated_vector(CPUState *env, uint32_t vector)
> @@ -255,3 +270,326 @@ void do_interrupt(CPUState *env)
>     }
>     check_interrupts(env);
>  }
> +
> +static void reset_tlb_mmu_all_ways(CPUState *env,
> +        const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
> +{
> +    unsigned wi, ei;
> +
> +    for (wi = 0; wi < tlb->nways; ++wi) {
> +        for (ei = 0; ei < tlb->way_size[wi]; ++ei) {
> +            entry[wi][ei].asid = 0;
> +            entry[wi][ei].variable = true;
> +        }
> +    }
> +}
> +
> +static void reset_tlb_mmu_ways56(CPUState *env,
> +        const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
> +{
> +    if (!tlb->varway56) {
> +        static const xtensa_tlb_entry way5[] = {
> +            {
> +                .vaddr = 0xd0000000,
> +                .paddr = 0,
> +                .asid = 1,
> +                .attr = 7,
> +                .variable = false,
> +            }, {
> +                .vaddr = 0xd8000000,
> +                .paddr = 0,
> +                .asid = 1,
> +                .attr = 3,
> +                .variable = false,
> +            }
> +        };
> +        static const xtensa_tlb_entry way6[] = {
> +            {
> +                .vaddr = 0xe0000000,
> +                .paddr = 0xf0000000,
> +                .asid = 1,
> +                .attr = 7,
> +                .variable = false,
> +            }, {
> +                .vaddr = 0xf0000000,
> +                .paddr = 0xf0000000,
> +                .asid = 1,
> +                .attr = 3,
> +                .variable = false,
> +            }
> +        };
> +        memcpy(entry[5], way5, sizeof(way5));
> +        memcpy(entry[6], way6, sizeof(way6));
> +    } else {
> +        uint32_t ei;
> +        for (ei = 0; ei < 8; ++ei) {
> +            entry[6][ei].vaddr = ei << 29;
> +            entry[6][ei].paddr = ei << 29;
> +            entry[6][ei].asid = 1;
> +            entry[6][ei].attr = 2;
> +        }
> +    }
> +}
> +
> +static void reset_tlb_region_way0(CPUState *env,
> +        xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
> +{
> +    unsigned ei;
> +
> +    for (ei = 0; ei < 8; ++ei) {
> +        entry[0][ei].vaddr = ei << 29;
> +        entry[0][ei].paddr = ei << 29;
> +        entry[0][ei].asid = 1;
> +        entry[0][ei].attr = 2;
> +        entry[0][ei].variable = true;
> +    }
> +}
> +
> +static void reset_mmu(CPUState *env)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        env->sregs[RASID] = 0x04030201;
> +        env->sregs[ITLBCFG] = 0;
> +        env->sregs[DTLBCFG] = 0;
> +        env->autorefill_idx = 0;
> +        reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb);
> +        reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb);
> +        reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb);
> +        reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb);
> +    } else {
> +        reset_tlb_region_way0(env, env->itlb);
> +        reset_tlb_region_way0(env, env->dtlb);
> +    }
> +}
> +
> +static unsigned get_ring(const CPUState *env, uint8_t asid)
> +{
> +    unsigned i;
> +    for (i = 0; i < 4; ++i) {
> +        if (((env->sregs[RASID] >> i * 8) & 0xff) == asid) {
> +            return i;
> +        }
> +    }
> +    return 0xff;
> +}
> +
> +/*!
> + * Lookup xtensa TLB for the given virtual address.
> + * See ISA, 4.6.2.2
> + *
> + * \param _wi: [out] way index
> + * \param _ei: [out] entry index
> + * \param _ring: [out] access ring
> + * \return 0 if ok, exception cause code otherwise
> + */
> +int xtensa_tlb_lookup(const CPUState *env, uint32_t addr, bool dtlb,
> +        uint32_t *_wi, uint32_t *_ei, uint8_t *_ring)

Underscores.

> +{
> +    const xtensa_tlb *tlb = dtlb ?
> +        &env->config->dtlb : &env->config->itlb;
> +    const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ?
> +        env->dtlb : env->itlb;
> +
> +    int nhits = 0;
> +    unsigned wi;
> +
> +    for (wi = 0; wi < tlb->nways; ++wi) {
> +        uint32_t vpn;
> +        uint32_t ei;
> +        split_tlb_entry_spec_way(env, addr, dtlb, &vpn, wi, &ei);
> +        if (entry[wi][ei].vaddr == vpn && entry[wi][ei].asid) {
> +            unsigned ring = get_ring(env, entry[wi][ei].asid);
> +            if (ring < 4) {
> +                if (++nhits > 1) {
> +                    return dtlb ?
> +                        LOAD_STORE_TLB_MULTI_HIT_CAUSE :
> +                        INST_TLB_MULTI_HIT_CAUSE;
> +                }
> +                *_wi = wi;
> +                *_ei = ei;
> +                *_ring = ring;
> +            }
> +        }
> +    }
> +    return nhits ? 0 :
> +        (dtlb ? LOAD_STORE_TLB_MISS_CAUSE : INST_TLB_MISS_CAUSE);
> +}
> +
> +/*!
> + * Convert MMU ATTR to PAGE_{READ,WRITE,EXEC} mask.
> + * See ISA, 4.6.5.10
> + */
> +static unsigned mmu_attr_to_access(uint32_t attr)
> +{
> +    unsigned access = 0;
> +    if (attr < 12) {
> +        access |= PAGE_READ;
> +        if (attr & 0x1) {
> +            access |= PAGE_EXEC;
> +        }
> +        if (attr & 0x2) {
> +            access |= PAGE_WRITE;
> +        }
> +    } else if (attr == 13) {
> +        access |= PAGE_READ | PAGE_WRITE;
> +    }
> +    return access;
> +}
> +
> +/*!
> + * Convert region protection ATTR to PAGE_{READ,WRITE,EXEC} mask.
> + * See ISA, 4.6.3.3
> + */
> +static unsigned region_attr_to_access(uint32_t attr)
> +{
> +    unsigned access = 0;
> +    if ((attr < 6 && attr != 3) || attr == 14) {
> +        access |= PAGE_READ | PAGE_WRITE;
> +    }
> +    if (attr > 0 && attr < 6) {
> +        access |= PAGE_EXEC;
> +    }
> +    return access;
> +}
> +
> +static bool is_access_granted(unsigned access, int is_write)
> +{
> +    switch (is_write) {
> +    case 0:
> +        return access & PAGE_READ;
> +
> +    case 1:
> +        return access & PAGE_WRITE;
> +
> +    case 2:
> +        return access & PAGE_EXEC;
> +
> +    default:
> +        return 0;
> +    }
> +}
> +
> +static int autorefill_mmu(CPUState *env, uint32_t vaddr, bool dtlb,
> +        uint32_t *wi, uint32_t *ei, uint8_t *ring);
> +
> +static int get_physical_addr_mmu(CPUState *env,
> +        uint32_t vaddr, int is_write, int mmu_idx,
> +        uint32_t *paddr, uint32_t *page_size, unsigned *access)
> +{
> +    bool dtlb = is_write != 2;
> +    uint32_t wi;
> +    uint32_t ei;
> +    uint8_t ring;
> +    int ret = xtensa_tlb_lookup(env, vaddr, dtlb, &wi, &ei, &ring);
> +
> +    if ((ret == INST_TLB_MISS_CAUSE || ret == LOAD_STORE_TLB_MISS_CAUSE) &&
> +            (mmu_idx != 0 || ((vaddr ^ env->sregs[PTEVADDR]) & 0xffc00000)) 
> &&
> +            autorefill_mmu(env, vaddr, dtlb, &wi, &ei, &ring) == 0) {
> +        ret = 0;
> +    }
> +    if (ret != 0) {
> +        return ret;
> +    }
> +
> +    const xtensa_tlb_entry *entry =
> +        xtensa_tlb_get_entry(env, dtlb, wi, ei);
> +
> +    if (ring < mmu_idx) {
> +        return dtlb ?
> +            LOAD_STORE_PRIVILEGE_CAUSE :
> +            INST_FETCH_PRIVILEGE_CAUSE;
> +    }
> +
> +    *access = mmu_attr_to_access(entry->attr);
> +    if (!is_access_granted(*access, is_write)) {
> +        return dtlb ?
> +            (is_write ?
> +             STORE_PROHIBITED_CAUSE :
> +             LOAD_PROHIBITED_CAUSE) :
> +            INST_FETCH_PROHIBITED_CAUSE;
> +    }
> +
> +    *paddr = entry->paddr | (vaddr & ~xtensa_tlb_get_addr_mask(env, dtlb, 
> wi));
> +    *page_size = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1;
> +
> +    return 0;
> +}
> +
> +static int autorefill_mmu(CPUState *env, uint32_t vaddr, bool dtlb,
> +        uint32_t *wi, uint32_t *ei, uint8_t *ring)
> +{
> +    uint32_t paddr;
> +    uint32_t page_size;
> +    unsigned access;
> +    uint32_t pt_vaddr =
> +        (env->sregs[PTEVADDR] | (vaddr >> 10)) & 0xfffffffc;
> +    int ret = get_physical_addr_mmu(env, pt_vaddr, 0, 0,
> +            &paddr, &page_size, &access);
> +
> +    qemu_log("%s: trying autorefill(%08x) -> %08x\n", __func__,
> +            vaddr, ret ? ~0 : paddr);
> +
> +    if (ret == 0) {
> +        uint32_t vpn;
> +        uint32_t pte = ldl_phys(paddr);
> +
> +        *ring = (pte >> 4) & 0x3;
> +        *wi = (++env->autorefill_idx) & 0x3;
> +        split_tlb_entry_spec_way(env, vaddr, dtlb, &vpn, *wi, ei);
> +        xtensa_tlb_set_entry(env, dtlb, *wi, *ei, vpn, pte);
> +        qemu_log("%s: autorefill(%08x): %08x -> %08x\n",
> +                __func__, vaddr, vpn, pte);
> +    }
> +    return ret;
> +}
> +
> +static int get_physical_addr_region(CPUState *env,
> +        uint32_t vaddr, int is_write, int mmu_idx,
> +        uint32_t *paddr, uint32_t *page_size, unsigned *access)
> +{
> +    bool dtlb = is_write != 2;
> +    uint32_t wi = 0;
> +    uint32_t ei = (vaddr >> 29) & 0x7;
> +    const xtensa_tlb_entry *entry =
> +        xtensa_tlb_get_entry(env, dtlb, wi, ei);
> +
> +    *access = region_attr_to_access(entry->attr);
> +    if (!is_access_granted(*access, is_write)) {
> +        return dtlb ?
> +            (is_write ?
> +             STORE_PROHIBITED_CAUSE :
> +             LOAD_PROHIBITED_CAUSE) :
> +            INST_FETCH_PROHIBITED_CAUSE;
> +    }
> +
> +    *paddr = entry->paddr | (vaddr & ~REGION_PAGE_MASK);
> +    *page_size = ~REGION_PAGE_MASK + 1;
> +
> +    return 0;
> +}
> +
> +/*!
> + * Convert virtual address to physical addr.
> + * MMU may issue pagewalk and change xtensa autorefill TLB way entry.
> + *
> + * \return 0 if ok, exception cause code otherwise
> + */
> +int xtensa_get_physical_addr(CPUState *env,
> +        uint32_t vaddr, int is_write, int mmu_idx,
> +        uint32_t *paddr, uint32_t *page_size, unsigned *access)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        return get_physical_addr_mmu(env, vaddr, is_write, mmu_idx,
> +                paddr, page_size, access);
> +    } else if (xtensa_option_bits_enabled(env->config,
> +                XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
> +                XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) {
> +        return get_physical_addr_region(env, vaddr, is_write, mmu_idx,
> +                paddr, page_size, access);
> +    } else {
> +        *paddr = vaddr;
> +        *page_size = TARGET_PAGE_SIZE;
> +        *access = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
> +        return 0;
> +    }
> +}
> diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h
> index 28689c3..09ab332 100644
> --- a/target-xtensa/helpers.h
> +++ b/target-xtensa/helpers.h
> @@ -22,4 +22,11 @@ DEF_HELPER_2(timer_irq, void, i32, i32)
>  DEF_HELPER_1(advance_ccount, void, i32)
>  DEF_HELPER_1(check_interrupts, void, env)
>
> +DEF_HELPER_1(wsr_rasid, void, i32)
> +DEF_HELPER_2(rtlb0, i32, i32, i32)
> +DEF_HELPER_2(rtlb1, i32, i32, i32)
> +DEF_HELPER_2(itlb, void, i32, i32)
> +DEF_HELPER_2(ptlb, i32, i32, i32)
> +DEF_HELPER_3(wtlb, void, i32, i32, i32)
> +
>  #include "def-helper.h"
> diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
> index fcec506..6a9c886 100644
> --- a/target-xtensa/op_helper.c
> +++ b/target-xtensa/op_helper.c
> @@ -70,13 +70,32 @@ static void do_unaligned_access(target_ulong addr, int 
> is_write, int is_user,
>     }
>  }
>
> -void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
> +void tlb_fill(target_ulong vaddr, int is_write, int mmu_idx, void *retaddr)
>  {
> -    tlb_set_page(cpu_single_env,
> -            addr & ~(TARGET_PAGE_SIZE - 1),
> -            addr & ~(TARGET_PAGE_SIZE - 1),
> -            PAGE_READ | PAGE_WRITE | PAGE_EXEC,
> -            mmu_idx, TARGET_PAGE_SIZE);
> +    CPUState *saved_env = env;
> +
> +    env = cpu_single_env;
> +    {
> +        uint32_t paddr;
> +        uint32_t page_size;
> +        unsigned access;
> +        int ret = xtensa_get_physical_addr(env, vaddr, is_write, mmu_idx,
> +                &paddr, &page_size, &access);
> +
> +        qemu_log("%s(%08x, %d, %d) -> %08x, ret = %d\n", __func__,
> +                vaddr, is_write, mmu_idx, paddr, ret);
> +
> +        if (ret == 0) {
> +            tlb_set_page(env,
> +                    vaddr & TARGET_PAGE_MASK,
> +                    paddr & TARGET_PAGE_MASK,
> +                    access, mmu_idx, page_size);
> +        } else {
> +            do_restore_state(retaddr);
> +            HELPER(exception_cause_vaddr)(env->pc, ret, vaddr);
> +        }
> +    }
> +    env = saved_env;
>  }
>
>  void HELPER(exception)(uint32_t excp)
> @@ -377,3 +396,273 @@ void HELPER(check_interrupts)(CPUState *env)
>  {
>     check_interrupts(env);
>  }
> +
> +void HELPER(wsr_rasid)(uint32_t v)
> +{
> +    v = (v & 0xffffff00) | 0x1;
> +    if (v != env->sregs[RASID]) {
> +        env->sregs[RASID] = v;
> +        tlb_flush(env, 1);
> +    }
> +}
> +
> +static uint32_t get_page_size(const CPUState *env, bool dtlb, uint32_t way)
> +{
> +    uint32_t tlbcfg = env->sregs[dtlb ? DTLBCFG : ITLBCFG];
> +
> +    switch (way) {
> +    case 4:
> +        return (tlbcfg >> 16) & 0x3;
> +
> +    case 5:
> +        return (tlbcfg >> 20) & 0x1;
> +
> +    case 6:
> +        return (tlbcfg >> 24) & 0x1;
> +
> +    default:
> +        return 0;
> +    }
> +}
> +
> +/*!
> + * Get bit mask for the virtual address bits translated by the TLB way
> + */
> +uint32_t xtensa_tlb_get_addr_mask(const CPUState *env, bool dtlb, uint32_t 
> way)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        bool varway56 = dtlb ?
> +            env->config->dtlb.varway56 :
> +            env->config->itlb.varway56;
> +
> +        switch (way) {
> +        case 4:
> +            return 0xfff00000 << get_page_size(env, dtlb, way) * 2;
> +
> +        case 5:
> +            if (varway56) {
> +                return 0xf8000000 << get_page_size(env, dtlb, way);
> +            } else {
> +                return 0xf8000000;
> +            }
> +
> +        case 6:
> +            if (varway56) {
> +                return 0xf0000000 << (1 - get_page_size(env, dtlb, way));
> +            } else {
> +                return 0xf0000000;
> +            }
> +
> +        default:
> +            return 0xfffff000;
> +        }
> +    } else {
> +        return REGION_PAGE_MASK;
> +    }
> +}
> +
> +/*!
> + * Get bit mask for the 'VPN without index' field.
> + * See ISA, 4.6.5.6, data format for RxTLB0
> + */
> +static uint32_t get_vpn_mask(const CPUState *env, bool dtlb, uint32_t way)
> +{
> +    if (way < 4) {
> +        bool is32 = (dtlb ?
> +                env->config->dtlb.nrefillentries :
> +                env->config->itlb.nrefillentries) == 32;
> +        return is32 ? 0xffff8000 : 0xffffc000;
> +    } else if (way == 4) {
> +        return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2;
> +    } else if (way <= 6) {
> +        uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way);
> +        bool varway56 = dtlb ?
> +            env->config->dtlb.varway56 :
> +            env->config->itlb.varway56;
> +
> +        if (varway56) {
> +            return mask << (way == 5 ? 2 : 3);
> +        } else {
> +            return mask << 1;
> +        }
> +    } else {
> +        return 0xfffff000;
> +    }
> +}
> +
> +/*!
> + * Split virtual address into VPN (with index) and entry index
> + * for the given TLB way
> + */
> +void split_tlb_entry_spec_way(const CPUState *env, uint32_t v, bool dtlb,
> +        uint32_t *vpn, uint32_t wi, uint32_t *ei)
> +{
> +    bool varway56 = dtlb ?
> +        env->config->dtlb.varway56 :
> +        env->config->itlb.varway56;
> +
> +    if (!dtlb) {
> +        wi &= 7;
> +    }
> +
> +    if (wi < 4) {
> +        bool is32 = (dtlb ?
> +                env->config->dtlb.nrefillentries :
> +                env->config->itlb.nrefillentries) == 32;
> +        *ei = (v >> 12) & (is32 ? 0x7 : 0x3);
> +    } else {
> +        switch (wi) {
> +        case 4:
> +            {
> +                uint32_t eibase = 20 + get_page_size(env, dtlb, wi) * 2;
> +                *ei = (v >> eibase) & 0x3;
> +            }
> +            break;
> +
> +        case 5:
> +            if (varway56) {
> +                uint32_t eibase = 27 + get_page_size(env, dtlb, wi);
> +                *ei = (v >> eibase) & 0x3;
> +            } else {
> +                *ei = (v >> 27) & 0x1;
> +            }
> +            break;
> +
> +        case 6:
> +            if (varway56) {
> +                uint32_t eibase = 29 - get_page_size(env, dtlb, wi);
> +                *ei = (v >> eibase) & 0x7;
> +            } else {
> +                *ei = (v >> 28) & 0x1;
> +            }
> +            break;
> +
> +        default:
> +            *ei = 0;
> +            break;
> +        }
> +    }
> +    *vpn = v & xtensa_tlb_get_addr_mask(env, dtlb, wi);
> +}
> +
> +/*!
> + * Split TLB address into TLB way, entry index and VPN (with index).
> + * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format
> + */
> +static void split_tlb_entry_spec(uint32_t v, bool dtlb,
> +        uint32_t *vpn, uint32_t *wi, uint32_t *ei)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        *wi = v & (dtlb ? 0xf : 0x7);
> +        split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
> +    } else {
> +        *vpn = v & REGION_PAGE_MASK;
> +        *wi = 0;
> +        *ei = (v >> 29) & 0x7;
> +    }
> +}
> +
> +static xtensa_tlb_entry *get_tlb_entry(uint32_t v, bool dtlb, uint32_t *_wi)
> +{
> +    uint32_t vpn;
> +    uint32_t wi;
> +    uint32_t ei;
> +
> +    split_tlb_entry_spec(v, dtlb, &vpn, &wi, &ei);
> +    if (_wi) {
> +        *_wi = wi;
> +    }
> +    return xtensa_tlb_get_entry(env, dtlb, wi, ei);
> +}
> +
> +uint32_t HELPER(rtlb0)(uint32_t v, uint32_t dtlb)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        uint32_t wi;
> +        const xtensa_tlb_entry *entry = get_tlb_entry(v, dtlb, &wi);
> +        return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
> +    } else {
> +        return v & REGION_PAGE_MASK;
> +    }
> +}
> +
> +uint32_t HELPER(rtlb1)(uint32_t v, uint32_t dtlb)
> +{
> +    const xtensa_tlb_entry *entry = get_tlb_entry(v, dtlb, NULL);
> +    return entry->paddr | entry->attr;
> +}
> +
> +void HELPER(itlb)(uint32_t v, uint32_t dtlb)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        uint32_t wi;
> +        xtensa_tlb_entry *entry = get_tlb_entry(v, dtlb, &wi);
> +        if (entry->variable && entry->asid) {
> +            tlb_flush_page(env, entry->vaddr);
> +            entry->asid = 0;
> +        }
> +    }
> +}
> +
> +uint32_t HELPER(ptlb)(uint32_t v, uint32_t dtlb)
> +{
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        uint32_t wi;
> +        uint32_t ei;
> +        uint8_t ring;
> +        int res = xtensa_tlb_lookup(env, v, dtlb, &wi, &ei, &ring);
> +
> +        switch (res) {
> +        case 0:
> +            if (ring >= xtensa_get_ring(env)) {
> +                return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8);
> +            }
> +            break;
> +
> +        case INST_TLB_MULTI_HIT_CAUSE:
> +        case LOAD_STORE_TLB_MULTI_HIT_CAUSE:
> +            HELPER(exception_cause_vaddr)(env->pc, res, v);
> +            break;
> +        }
> +        return 0;
> +    } else {
> +        return (v & REGION_PAGE_MASK) | 0x1;
> +    }
> +}
> +
> +void xtensa_tlb_set_entry(CPUState *env, bool dtlb,
> +        unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte)
> +{
> +    xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei);
> +
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
> +        if (entry->variable) {
> +            if (entry->asid) {
> +                tlb_flush_page(env, entry->vaddr);
> +            }
> +            entry->vaddr = vpn;
> +            entry->paddr = pte & xtensa_tlb_get_addr_mask(env, dtlb, wi);
> +            entry->asid = (env->sregs[RASID] >> ((pte >> 1) & 0x18)) & 0xff;
> +            entry->attr = pte & 0xf;
> +        } else {
> +            qemu_log("%s %d, %d, %d trying to set immutable entry\n",
> +                    __func__, dtlb, wi, ei);
> +        }
> +    } else {
> +        tlb_flush_page(env, entry->vaddr);
> +        if (xtensa_option_enabled(env->config,
> +                    XTENSA_OPTION_REGION_TRANSLATION)) {
> +            entry->paddr = pte & REGION_PAGE_MASK;
> +        }
> +        entry->attr = pte & 0xf;
> +    }
> +}
> +
> +void HELPER(wtlb)(uint32_t p, uint32_t v, uint32_t dtlb)
> +{
> +    uint32_t vpn;
> +    uint32_t wi;
> +    uint32_t ei;
> +    split_tlb_entry_spec(v, dtlb, &vpn, &wi, &ei);
> +    xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
> +}
> diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c
> index 3b21bc4..731f04f 100644
> --- a/target-xtensa/translate.c
> +++ b/target-xtensa/translate.c
> @@ -80,6 +80,10 @@ static const char * const sregnames[256] = {
>     [SCOMPARE1] = "SCOMPARE1",
>     [WINDOW_BASE] = "WINDOW_BASE",
>     [WINDOW_START] = "WINDOW_START",
> +    [PTEVADDR] = "PTEVADDR",
> +    [RASID] = "RASID",
> +    [ITLBCFG] = "ITLBCFG",
> +    [DTLBCFG] = "DTLBCFG",
>     [EPC1] = "EPC1",
>     [EPC1 + 1] = "EPC2",
>     [EPC1 + 2] = "EPC3",
> @@ -161,6 +165,11 @@ void xtensa_translate_init(void)
>  #include "helpers.h"
>  }
>
> +static inline bool option_bits_enabled(DisasContext *dc, uint64_t opt)
> +{
> +    return xtensa_option_bits_enabled(dc->config, opt);
> +}
> +
>  static inline bool option_enabled(DisasContext *dc, int opt)
>  {
>     return xtensa_option_enabled(dc->config, opt);
> @@ -379,11 +388,19 @@ static void gen_rsr_ccount(DisasContext *dc, TCGv_i32 
> d, uint32_t sr)
>     tcg_gen_mov_i32(d, cpu_SR[sr]);
>  }
>
> +static void gen_rsr_ptevaddr(DisasContext *dc, TCGv_i32 d, uint32_t sr)
> +{
> +    tcg_gen_shri_i32(d, cpu_SR[EXCVADDR], 10);
> +    tcg_gen_or_i32(d, d, cpu_SR[sr]);
> +    tcg_gen_andi_i32(d, d, 0xfffffffc);
> +}
> +
>  static void gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr)
>  {
>     static void (* const rsr_handler[256])(DisasContext *dc,
>             TCGv_i32 d, uint32_t sr) = {
>         [CCOUNT] = gen_rsr_ccount,
> +        [PTEVADDR] = gen_rsr_ptevaddr,
>     };
>
>     if (sregnames[sr]) {
> @@ -436,6 +453,23 @@ static void gen_wsr_windowstart(DisasContext *dc, 
> uint32_t sr, TCGv_i32 v)
>     reset_used_window(dc);
>  }
>
> +static void gen_wsr_ptevaddr(DisasContext *dc, uint32_t sr, TCGv_i32 v)
> +{
> +    tcg_gen_andi_i32(cpu_SR[sr], v, 0xffc00000);
> +}
> +
> +static void gen_wsr_rasid(DisasContext *dc, uint32_t sr, TCGv_i32 v)
> +{
> +    gen_helper_wsr_rasid(v);
> +    /* This can change tb->flags, so exit tb */
> +    gen_jumpi_check_loop_end(dc, -1);
> +}
> +
> +static void gen_wsr_tlbcfg(DisasContext *dc, uint32_t sr, TCGv_i32 v)
> +{
> +    tcg_gen_andi_i32(cpu_SR[sr], v, 0x01130000);
> +}
> +
>  static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v)
>  {
>     tcg_gen_andi_i32(cpu_SR[sr], v,
> @@ -505,6 +539,10 @@ static void gen_wsr(DisasContext *dc, uint32_t sr, 
> TCGv_i32 s)
>         [LITBASE] = gen_wsr_litbase,
>         [WINDOW_BASE] = gen_wsr_windowbase,
>         [WINDOW_START] = gen_wsr_windowstart,
> +        [PTEVADDR] = gen_wsr_ptevaddr,
> +        [RASID] = gen_wsr_rasid,
> +        [ITLBCFG] = gen_wsr_tlbcfg,
> +        [DTLBCFG] = gen_wsr_tlbcfg,
>         [INTSET] = gen_wsr_intset,
>         [INTCLEAR] = gen_wsr_intclear,
>         [INTENABLE] = gen_wsr_intenable,
> @@ -585,14 +623,16 @@ static void gen_window_check3(DisasContext *dc, 
> unsigned r1, unsigned r2,
>
>  static void disas_xtensa_insn(DisasContext *dc)
>  {
> -#define HAS_OPTION(opt) do { \
> -        if (!option_enabled(dc, opt)) { \
> -            qemu_log("Option %d is not enabled %s:%d\n", \
> -                    (opt), __FILE__, __LINE__); \
> +#define HAS_OPTION_BITS(opt) do { \
> +        if (!option_bits_enabled(dc, opt)) { \
> +            qemu_log("Option is not enabled %s:%d\n", \
> +                    __FILE__, __LINE__); \
>             goto invalid_opcode; \
>         } \
>     } while (0)
>
> +#define HAS_OPTION(opt) HAS_OPTION_BITS(XTENSA_OPTION_BIT(opt))
> +
>  #define TBD() qemu_log("TBD(pc = %08x): %s:%d\n", dc->pc, __FILE__, __LINE__)
>  #define RESERVED() do { \
>         qemu_log("RESERVED(pc = %08x, %02x%02x%02x): %s:%d\n", \
> @@ -1055,7 +1095,48 @@ static void disas_xtensa_insn(DisasContext *dc)
>                 break;
>
>             case 5: /*TLB*/
> -                TBD();
> +                HAS_OPTION_BITS(
> +                        XTENSA_OPTION_BIT(XTENSA_OPTION_MMU) |
> +                        XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
> +                        XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION));
> +                gen_check_privilege(dc);
> +                gen_window_check2(dc, RRR_S, RRR_T);
> +                {
> +                    TCGv_i32 dtlb = tcg_const_i32((RRR_R & 8) != 0);
> +
> +                    switch (RRR_R & 7) {
> +                    case 3: /*RITLB0*/ /*RDTLB0*/
> +                        gen_helper_rtlb0(cpu_R[RRR_T], cpu_R[RRR_S], dtlb);
> +                        break;
> +
> +                    case 4: /*IITLB*/ /*IDTLB*/
> +                        gen_helper_itlb(cpu_R[RRR_S], dtlb);
> +                        /* This could change memory mapping, so exit tb */
> +                        gen_jumpi_check_loop_end(dc, -1);
> +                        break;
> +
> +                    case 5: /*PITLB*/ /*PDTLB*/
> +                        tcg_gen_movi_i32(cpu_pc, dc->pc);
> +                        gen_helper_ptlb(cpu_R[RRR_T], cpu_R[RRR_S], dtlb);
> +                        break;
> +
> +                    case 6: /*WITLB*/ /*WDTLB*/
> +                        gen_helper_wtlb(cpu_R[RRR_T], cpu_R[RRR_S], dtlb);
> +                        /* This could change memory mapping, so exit tb */
> +                        gen_jumpi_check_loop_end(dc, -1);
> +                        break;
> +
> +                    case 7: /*RITLB1*/ /*RDTLB1*/
> +                        gen_helper_rtlb1(cpu_R[RRR_T], cpu_R[RRR_S], dtlb);
> +                        break;
> +
> +                    default:
> +                        tcg_temp_free(dtlb);
> +                        RESERVED();
> +                        break;
> +                    }
> +                    tcg_temp_free(dtlb);
> +                }
>                 break;
>
>             case 6: /*RT0*/
> --
> 1.7.6
>
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]