cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

mmu.c (10091B)


      1/*
      2 *  CRIS mmu emulation.
      3 *
      4 *  Copyright (c) 2007 AXIS Communications AB
      5 *  Written by Edgar E. Iglesias.
      6 *
      7 * This library is free software; you can redistribute it and/or
      8 * modify it under the terms of the GNU Lesser General Public
      9 * License as published by the Free Software Foundation; either
     10 * version 2.1 of the License, or (at your option) any later version.
     11 *
     12 * This library is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15 * Lesser General Public License for more details.
     16 *
     17 * You should have received a copy of the GNU Lesser General Public
     18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     19 */
     20
     21#include "qemu/osdep.h"
     22#include "cpu.h"
     23#include "exec/exec-all.h"
     24#include "mmu.h"
     25
     26#ifdef DEBUG
     27#define D(x) x
     28#define D_LOG(...) qemu_log(__VA_ARGS__)
     29#else
     30#define D(x) do { } while (0)
     31#define D_LOG(...) do { } while (0)
     32#endif
     33
     34void cris_mmu_init(CPUCRISState *env)
     35{
     36    env->mmu_rand_lfsr = 0xcccc;
     37}
     38
     39#define SR_POLYNOM 0x8805
     40static inline unsigned int compute_polynom(unsigned int sr)
     41{
     42    unsigned int i;
     43    unsigned int f;
     44
     45    f = 0;
     46    for (i = 0; i < 16; i++) {
     47        f += ((SR_POLYNOM >> i) & 1) & ((sr >> i) & 1);
     48    }
     49
     50    return f;
     51}
     52
     53static void cris_mmu_update_rand_lfsr(CPUCRISState *env)
     54{
     55    unsigned int f;
     56
     57    /* Update lfsr at every fault.  */
     58    f = compute_polynom(env->mmu_rand_lfsr);
     59    env->mmu_rand_lfsr >>= 1;
     60    env->mmu_rand_lfsr |= (f << 15);
     61    env->mmu_rand_lfsr &= 0xffff;
     62}
     63
     64static inline int cris_mmu_enabled(uint32_t rw_gc_cfg)
     65{
     66    return (rw_gc_cfg & 12) != 0;
     67}
     68
     69static inline int cris_mmu_segmented_addr(int seg, uint32_t rw_mm_cfg)
     70{
     71    return (1 << seg) & rw_mm_cfg;
     72}
     73
     74static uint32_t cris_mmu_translate_seg(CPUCRISState *env, int seg)
     75{
     76    uint32_t base;
     77    int i;
     78
     79    if (seg < 8) {
     80        base = env->sregs[SFR_RW_MM_KBASE_LO];
     81    } else {
     82        base = env->sregs[SFR_RW_MM_KBASE_HI];
     83    }
     84
     85    i = seg & 7;
     86    base >>= i * 4;
     87    base &= 15;
     88
     89    base <<= 28;
     90    return base;
     91}
     92
     93/* Used by the tlb decoder.  */
     94#define EXTRACT_FIELD(src, start, end)                  \
     95    (((src) >> start) & ((1 << (end - start + 1)) - 1))
     96
     97static inline void set_field(uint32_t *dst, unsigned int val,
     98			     unsigned int offset, unsigned int width)
     99{
    100    uint32_t mask;
    101
    102    mask = (1 << width) - 1;
    103    mask <<= offset;
    104    val <<= offset;
    105
    106    val &= mask;
    107    *dst &= ~(mask);
    108    *dst |= val;
    109}
    110
    111#ifdef DEBUG
    112static void dump_tlb(CPUCRISState *env, int mmu)
    113{
    114    int set;
    115    int idx;
    116    uint32_t hi, lo, tlb_vpn, tlb_pfn;
    117
    118    for (set = 0; set < 4; set++) {
    119        for (idx = 0; idx < 16; idx++) {
    120            lo = env->tlbsets[mmu][set][idx].lo;
    121            hi = env->tlbsets[mmu][set][idx].hi;
    122            tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
    123            tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
    124
    125            printf("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n",
    126                   set, idx, hi, lo, tlb_vpn, tlb_pfn);
    127        }
    128    }
    129}
    130#endif
    131
    132static int cris_mmu_translate_page(struct cris_mmu_result *res,
    133                                   CPUCRISState *env, uint32_t vaddr,
    134                                   MMUAccessType access_type,
    135                                   int usermode, int debug)
    136{
    137    unsigned int vpage;
    138    unsigned int idx;
    139    uint32_t pid, lo, hi;
    140    uint32_t tlb_vpn, tlb_pfn = 0;
    141    int tlb_pid, tlb_g, tlb_v, tlb_k, tlb_w, tlb_x;
    142    int cfg_v, cfg_k, cfg_w, cfg_x;
    143    int set, match = 0;
    144    uint32_t r_cause;
    145    uint32_t r_cfg;
    146    int rwcause;
    147    int mmu = 1; /* Data mmu is default.  */
    148    int vect_base;
    149
    150    r_cause = env->sregs[SFR_R_MM_CAUSE];
    151    r_cfg = env->sregs[SFR_RW_MM_CFG];
    152    pid = env->pregs[PR_PID] & 0xff;
    153
    154    switch (access_type) {
    155    case MMU_INST_FETCH:
    156        rwcause = CRIS_MMU_ERR_EXEC;
    157        mmu = 0;
    158        break;
    159    case MMU_DATA_STORE:
    160        rwcause = CRIS_MMU_ERR_WRITE;
    161        break;
    162    default:
    163    case MMU_DATA_LOAD:
    164        rwcause = CRIS_MMU_ERR_READ;
    165        break;
    166    }
    167
    168    /* I exception vectors 4 - 7, D 8 - 11.  */
    169    vect_base = (mmu + 1) * 4;
    170
    171    vpage = vaddr >> 13;
    172
    173    /*
    174     * We know the index which to check on each set.
    175     * Scan both I and D.
    176     */
    177    idx = vpage & 15;
    178    for (set = 0; set < 4; set++) {
    179        lo = env->tlbsets[mmu][set][idx].lo;
    180        hi = env->tlbsets[mmu][set][idx].hi;
    181
    182        tlb_vpn = hi >> 13;
    183        tlb_pid = EXTRACT_FIELD(hi, 0, 7);
    184        tlb_g  = EXTRACT_FIELD(lo, 4, 4);
    185
    186        D_LOG("TLB[%d][%d][%d] v=%x vpage=%x lo=%x hi=%x\n",
    187              mmu, set, idx, tlb_vpn, vpage, lo, hi);
    188        if ((tlb_g || (tlb_pid == pid)) && tlb_vpn == vpage) {
    189            match = 1;
    190            break;
    191        }
    192    }
    193
    194    res->bf_vec = vect_base;
    195    if (match) {
    196        cfg_w  = EXTRACT_FIELD(r_cfg, 19, 19);
    197        cfg_k  = EXTRACT_FIELD(r_cfg, 18, 18);
    198        cfg_x  = EXTRACT_FIELD(r_cfg, 17, 17);
    199        cfg_v  = EXTRACT_FIELD(r_cfg, 16, 16);
    200
    201        tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
    202        tlb_v = EXTRACT_FIELD(lo, 3, 3);
    203        tlb_k = EXTRACT_FIELD(lo, 2, 2);
    204        tlb_w = EXTRACT_FIELD(lo, 1, 1);
    205        tlb_x = EXTRACT_FIELD(lo, 0, 0);
    206
    207        /*
    208         * set_exception_vector(0x04, i_mmu_refill);
    209         * set_exception_vector(0x05, i_mmu_invalid);
    210         * set_exception_vector(0x06, i_mmu_access);
    211         * set_exception_vector(0x07, i_mmu_execute);
    212         * set_exception_vector(0x08, d_mmu_refill);
    213         * set_exception_vector(0x09, d_mmu_invalid);
    214         * set_exception_vector(0x0a, d_mmu_access);
    215         * set_exception_vector(0x0b, d_mmu_write);
    216         */
    217        if (cfg_k && tlb_k && usermode) {
    218            D(printf("tlb: kernel protected %x lo=%x pc=%x\n",
    219                     vaddr, lo, env->pc));
    220            match = 0;
    221            res->bf_vec = vect_base + 2;
    222        } else if (access_type == MMU_DATA_STORE && cfg_w && !tlb_w) {
    223            D(printf("tlb: write protected %x lo=%x pc=%x\n",
    224                     vaddr, lo, env->pc));
    225            match = 0;
    226            /* write accesses never go through the I mmu.  */
    227            res->bf_vec = vect_base + 3;
    228        } else if (access_type == MMU_INST_FETCH && cfg_x && !tlb_x) {
    229            D(printf("tlb: exec protected %x lo=%x pc=%x\n",
    230                     vaddr, lo, env->pc));
    231            match = 0;
    232            res->bf_vec = vect_base + 3;
    233        } else if (cfg_v && !tlb_v) {
    234            D(printf("tlb: invalid %x\n", vaddr));
    235            match = 0;
    236            res->bf_vec = vect_base + 1;
    237        }
    238
    239        res->prot = 0;
    240        if (match) {
    241            res->prot |= PAGE_READ;
    242            if (tlb_w) {
    243                res->prot |= PAGE_WRITE;
    244            }
    245            if (mmu == 0 && (cfg_x || tlb_x)) {
    246                res->prot |= PAGE_EXEC;
    247            }
    248        } else {
    249            D(dump_tlb(env, mmu));
    250        }
    251    } else {
    252        /* If refill, provide a randomized set.  */
    253        set = env->mmu_rand_lfsr & 3;
    254    }
    255
    256    if (!match && !debug) {
    257        cris_mmu_update_rand_lfsr(env);
    258
    259        /* Compute index.  */
    260        idx = vpage & 15;
    261
    262        /* Update RW_MM_TLB_SEL.  */
    263        env->sregs[SFR_RW_MM_TLB_SEL] = 0;
    264        set_field(&env->sregs[SFR_RW_MM_TLB_SEL], idx, 0, 4);
    265        set_field(&env->sregs[SFR_RW_MM_TLB_SEL], set, 4, 2);
    266
    267        /* Update RW_MM_CAUSE.  */
    268        set_field(&r_cause, rwcause, 8, 2);
    269        set_field(&r_cause, vpage, 13, 19);
    270        set_field(&r_cause, pid, 0, 8);
    271        env->sregs[SFR_R_MM_CAUSE] = r_cause;
    272        D(printf("refill vaddr=%x pc=%x\n", vaddr, env->pc));
    273    }
    274
    275    D(printf("%s access=%u mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x"
    276             " %x cause=%x sel=%x sp=%x %x %x\n",
    277             __func__, access_type, match, env->pc,
    278             vaddr, vpage,
    279             tlb_vpn, tlb_pfn, tlb_pid,
    280             pid,
    281             r_cause,
    282             env->sregs[SFR_RW_MM_TLB_SEL],
    283             env->regs[R_SP], env->pregs[PR_USP], env->ksp));
    284
    285    res->phy = tlb_pfn << TARGET_PAGE_BITS;
    286    return !match;
    287}
    288
    289void cris_mmu_flush_pid(CPUCRISState *env, uint32_t pid)
    290{
    291    target_ulong vaddr;
    292    unsigned int idx;
    293    uint32_t lo, hi;
    294    uint32_t tlb_vpn;
    295    int tlb_pid, tlb_g, tlb_v;
    296    unsigned int set;
    297    unsigned int mmu;
    298
    299    pid &= 0xff;
    300    for (mmu = 0; mmu < 2; mmu++) {
    301        for (set = 0; set < 4; set++) {
    302            for (idx = 0; idx < 16; idx++) {
    303                lo = env->tlbsets[mmu][set][idx].lo;
    304                hi = env->tlbsets[mmu][set][idx].hi;
    305
    306                tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
    307                tlb_pid = EXTRACT_FIELD(hi, 0, 7);
    308                tlb_g  = EXTRACT_FIELD(lo, 4, 4);
    309                tlb_v = EXTRACT_FIELD(lo, 3, 3);
    310
    311                if (tlb_v && !tlb_g && (tlb_pid == pid)) {
    312                    vaddr = tlb_vpn << TARGET_PAGE_BITS;
    313                    D_LOG("flush pid=%x vaddr=%x\n", pid, vaddr);
    314                    tlb_flush_page(env_cpu(env), vaddr);
    315                }
    316            }
    317        }
    318    }
    319}
    320
    321int cris_mmu_translate(struct cris_mmu_result *res,
    322                       CPUCRISState *env, uint32_t vaddr,
    323                       MMUAccessType access_type, int mmu_idx, int debug)
    324{
    325    int seg;
    326    int miss = 0;
    327    int is_user = mmu_idx == MMU_USER_IDX;
    328    uint32_t old_srs;
    329
    330    old_srs = env->pregs[PR_SRS];
    331
    332    env->pregs[PR_SRS] = access_type == MMU_INST_FETCH ? 1 : 2;
    333
    334    if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) {
    335        res->phy = vaddr;
    336        res->prot = PAGE_BITS;
    337        goto done;
    338    }
    339
    340    seg = vaddr >> 28;
    341    if (!is_user && cris_mmu_segmented_addr(seg, env->sregs[SFR_RW_MM_CFG])) {
    342        uint32_t base;
    343
    344        miss = 0;
    345        base = cris_mmu_translate_seg(env, seg);
    346        res->phy = base | (0x0fffffff & vaddr);
    347        res->prot = PAGE_BITS;
    348    } else {
    349        miss = cris_mmu_translate_page(res, env, vaddr, access_type,
    350                                       is_user, debug);
    351    }
    352 done:
    353    env->pregs[PR_SRS] = old_srs;
    354    return miss;
    355}