Commit dcf848c4 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20150316' into staging



target-arm queue:
 * fix handling of execute-never bits in page table walks
 * tell kernel to initialize KVM GIC in realize function
 * fix handling of STM (user) with r15 in register list
 * ignore low bit of PC in M-profile exception return
 * fix linux-user get/set_tls syscalls on CPUs with TZ

# gpg: Signature made Mon Mar 16 12:39:04 2015 GMT using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"

* remotes/pmaydell/tags/pull-target-arm-20150316:
  linux-user: Access correct register for get/set_tls syscalls on ARM TZ CPUs
  target-arm: Ignore low bit of PC in M-profile exception return
  target-arm: Fix handling of STM (user) with r15 in register list
  hw/intc/arm_gic: Initialize the vgic in the realize function
  target-arm: get_phys_addr_lpae: more xn control
  target-arm: fix get_phys_addr_v6/SCTLR_AFE access check
  target-arm: convert check_ap to ap_to_rw_prot

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 307146cb b8d43285
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -573,6 +573,13 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
        kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1);
    }

    /* Tell the kernel to complete VGIC initialization now */
    if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
                              KVM_DEV_ARM_VGIC_CTRL_INIT)) {
        kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
                          KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1);
    }

    /* Distributor */
    memory_region_init_reservation(&s->iomem, OBJECT(s),
                                   "kvm-gic_dist", 0x1000);
+14 −1
Original line number Diff line number Diff line
@@ -29,7 +29,20 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp)

static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls)
{
    if (access_secure_reg(env)) {
        env->cp15.tpidruro_s = newtls;
    } else {
        env->cp15.tpidrro_el[0] = newtls;
    }
}

static inline target_ulong cpu_get_tls(CPUARMState *env)
{
    if (access_secure_reg(env)) {
        return env->cp15.tpidruro_s;
    } else {
        return env->cp15.tpidrro_el[0];
    }
}

#endif
+1 −1
Original line number Diff line number Diff line
@@ -566,7 +566,7 @@ do_kernel_trap(CPUARMState *env)
        end_exclusive();
        break;
    case 0xffff0fe0: /* __kernel_get_tls */
        env->regs[0] = env->cp15.tpidrro_el[0];
        env->regs[0] = cpu_get_tls(env);
        break;
    case 0xffff0f60: /* __kernel_cmpxchg64 */
        arm_kernel_cmpxchg64_helper(env);
+163 −59
Original line number Diff line number Diff line
@@ -4334,6 +4334,16 @@ static void do_v7m_exception_exit(CPUARMState *env)
    env->regs[12] = v7m_pop(env);
    env->regs[14] = v7m_pop(env);
    env->regs[15] = v7m_pop(env);
    if (env->regs[15] & 1) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "M profile return from interrupt with misaligned "
                      "PC is UNPREDICTABLE\n");
        /* Actual hardware seems to ignore the lsbit, and there are several
         * RTOSes out there which incorrectly assume the r15 in the stack
         * frame should be a Thumb-style "lsbit indicates ARM/Thumb" value.
         */
        env->regs[15] &= ~1U;
    }
    xpsr = v7m_pop(env);
    xpsr_write(env, xpsr, 0xfffffdff);
    /* Undo stack alignment.  */
@@ -4903,34 +4913,28 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx)
    }
}

/* Check section/page access permissions.
   Returns the page protection flags, or zero if the access is not
   permitted.  */
static inline int check_ap(CPUARMState *env, ARMMMUIdx mmu_idx,
                           int ap, int domain_prot,
                           int access_type)
/* Translate section/page access permissions to page
 * R/W protection flags
 *
 * @env:         CPUARMState
 * @mmu_idx:     MMU index indicating required translation regime
 * @ap:          The 3-bit access permissions (AP[2:0])
 * @domain_prot: The 2-bit domain access permissions
 */
static inline int ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx,
                                int ap, int domain_prot)
{
    int prot_ro;
    bool is_user = regime_is_user(env, mmu_idx);

    if (domain_prot == 3) {
        return PAGE_READ | PAGE_WRITE;
    }

    if (access_type == 1) {
        prot_ro = 0;
    } else {
        prot_ro = PAGE_READ;
    }

    switch (ap) {
    case 0:
        if (arm_feature(env, ARM_FEATURE_V7)) {
            return 0;
        }
        if (access_type == 1) {
            return 0;
        }
        switch (regime_sctlr(env, mmu_idx) & (SCTLR_S | SCTLR_R)) {
        case SCTLR_S:
            return is_user ? 0 : PAGE_READ;
@@ -4943,7 +4947,7 @@ static inline int check_ap(CPUARMState *env, ARMMMUIdx mmu_idx,
        return is_user ? 0 : PAGE_READ | PAGE_WRITE;
    case 2:
        if (is_user) {
            return prot_ro;
            return PAGE_READ;
        } else {
            return PAGE_READ | PAGE_WRITE;
        }
@@ -4952,17 +4956,126 @@ static inline int check_ap(CPUARMState *env, ARMMMUIdx mmu_idx,
    case 4: /* Reserved.  */
        return 0;
    case 5:
        return is_user ? 0 : prot_ro;
        return is_user ? 0 : PAGE_READ;
    case 6:
        return prot_ro;
        return PAGE_READ;
    case 7:
        if (!arm_feature(env, ARM_FEATURE_V6K)) {
            return 0;
        }
        return prot_ro;
        return PAGE_READ;
    default:
        abort();
        g_assert_not_reached();
    }
}

/* Translate section/page access permissions to page
 * R/W protection flags.
 *
 * @ap:      The 2-bit simple AP (AP[2:1])
 * @is_user: TRUE if accessing from PL0
 */
static inline int simple_ap_to_rw_prot_is_user(int ap, bool is_user)
{
    switch (ap) {
    case 0:
        return is_user ? 0 : PAGE_READ | PAGE_WRITE;
    case 1:
        return PAGE_READ | PAGE_WRITE;
    case 2:
        return is_user ? 0 : PAGE_READ;
    case 3:
        return PAGE_READ;
    default:
        g_assert_not_reached();
    }
}

static inline int
simple_ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap)
{
    return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx));
}

/* Translate section/page access permissions to protection flags
 *
 * @env:     CPUARMState
 * @mmu_idx: MMU index indicating required translation regime
 * @is_aa64: TRUE if AArch64
 * @ap:      The 2-bit simple AP (AP[2:1])
 * @ns:      NS (non-secure) bit
 * @xn:      XN (execute-never) bit
 * @pxn:     PXN (privileged execute-never) bit
 */
static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64,
                      int ap, int ns, int xn, int pxn)
{
    bool is_user = regime_is_user(env, mmu_idx);
    int prot_rw, user_rw;
    bool have_wxn;
    int wxn = 0;

    assert(mmu_idx != ARMMMUIdx_S2NS);

    user_rw = simple_ap_to_rw_prot_is_user(ap, true);
    if (is_user) {
        prot_rw = user_rw;
    } else {
        prot_rw = simple_ap_to_rw_prot_is_user(ap, false);
    }

    if (ns && arm_is_secure(env) && (env->cp15.scr_el3 & SCR_SIF)) {
        return prot_rw;
    }

    /* TODO have_wxn should be replaced with
     *   ARM_FEATURE_V8 || (ARM_FEATURE_V7 && ARM_FEATURE_EL2)
     * when ARM_FEATURE_EL2 starts getting set. For now we assume all LPAE
     * compatible processors have EL2, which is required for [U]WXN.
     */
    have_wxn = arm_feature(env, ARM_FEATURE_LPAE);

    if (have_wxn) {
        wxn = regime_sctlr(env, mmu_idx) & SCTLR_WXN;
    }

    if (is_aa64) {
        switch (regime_el(env, mmu_idx)) {
        case 1:
            if (!is_user) {
                xn = pxn || (user_rw & PAGE_WRITE);
            }
            break;
        case 2:
        case 3:
            break;
        }
    } else if (arm_feature(env, ARM_FEATURE_V7)) {
        switch (regime_el(env, mmu_idx)) {
        case 1:
        case 3:
            if (is_user) {
                xn = xn || !(user_rw & PAGE_READ);
            } else {
                int uwxn = 0;
                if (have_wxn) {
                    uwxn = regime_sctlr(env, mmu_idx) & SCTLR_UWXN;
                }
                xn = xn || !(prot_rw & PAGE_READ) || pxn ||
                     (uwxn && (user_rw & PAGE_WRITE));
            }
            break;
        case 2:
            break;
        }
    } else {
        xn = wxn = 0;
    }

    if (xn || (wxn && (prot_rw & PAGE_WRITE))) {
        return prot_rw;
    }
    return prot_rw | PAGE_EXEC;
}

static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx,
@@ -5083,12 +5196,12 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
        }
        code = 15;
    }
    *prot = check_ap(env, mmu_idx, ap, domain_prot, access_type);
    if (!*prot) {
    *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot);
    *prot |= *prot ? PAGE_EXEC : 0;
    if (!(*prot & (1 << access_type))) {
        /* Access permission fault.  */
        goto do_fault;
    }
    *prot |= PAGE_EXEC;
    *phys_ptr = phys_addr;
    return 0;
do_fault:
@@ -5197,21 +5310,25 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
        if (xn && access_type == 2)
            goto do_fault;

        if (arm_feature(env, ARM_FEATURE_V6K) &&
                (regime_sctlr(env, mmu_idx) & SCTLR_AFE)) {
            /* The simplified model uses AP[0] as an access control bit.  */
        if ((regime_sctlr(env, mmu_idx) & SCTLR_AFE)
                && (ap & 1) == 0) {
            if ((ap & 1) == 0) {
                /* Access flag fault.  */
                code = (code == 15) ? 6 : 3;
                goto do_fault;
            }
        *prot = check_ap(env, mmu_idx, ap, domain_prot, access_type);
        if (!*prot) {
            /* Access permission fault.  */
            goto do_fault;
            *prot = simple_ap_to_rw_prot(env, mmu_idx, ap >> 1);
        } else {
            *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot);
        }
        if (!xn) {
        if (*prot && !xn) {
            *prot |= PAGE_EXEC;
        }
        if (!(*prot & (1 << access_type))) {
            /* Access permission fault.  */
            goto do_fault;
        }
    }
    *phys_ptr = phys_addr;
    return 0;
@@ -5249,8 +5366,8 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
    int32_t granule_sz = 9;
    int32_t va_size = 32;
    int32_t tbi = 0;
    bool is_user;
    TCR *tcr = regime_tcr(env, mmu_idx);
    int ap, ns, xn, pxn;

    /* TODO:
     * This code assumes we're either a 64-bit EL1 or a 32-bit PL1;
@@ -5411,7 +5528,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
        if (extract32(tableattrs, 2, 1)) {
            attrs &= ~(1 << 4);
        }
        /* Since we're always in the Non-secure state, NSTable is ignored. */
        attrs |= extract32(tableattrs, 4, 1) << 3; /* NS */
        break;
    }
    /* Here descaddr is the final physical address, and attributes
@@ -5422,31 +5539,18 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
        /* Access flag */
        goto do_fault;
    }

    ap = extract32(attrs, 4, 2);
    ns = extract32(attrs, 3, 1);
    xn = extract32(attrs, 12, 1);
    pxn = extract32(attrs, 11, 1);

    *prot = get_S1prot(env, mmu_idx, va_size == 64, ap, ns, xn, pxn);

    fault_type = permission_fault;
    is_user = regime_is_user(env, mmu_idx);
    if (is_user && !(attrs & (1 << 4))) {
        /* Unprivileged access not enabled */
        goto do_fault;
    }
    *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
    if ((arm_feature(env, ARM_FEATURE_V8) && is_user && (attrs & (1 << 12))) ||
        (!arm_feature(env, ARM_FEATURE_V8) && (attrs & (1 << 12))) ||
        (!is_user && (attrs & (1 << 11)))) {
        /* XN/UXN or PXN. Since we only implement EL0/EL1 we unconditionally
         * treat XN/UXN as UXN for v8.
         */
        if (access_type == 2) {
            goto do_fault;
        }
        *prot &= ~PAGE_EXEC;
    }
    if (attrs & (1 << 5)) {
        /* Write access forbidden */
        if (access_type == 1) {
    if (!(*prot & (1 << access_type))) {
        goto do_fault;
    }
        *prot &= ~PAGE_WRITE;
    }

    *phys_ptr = descaddr;
    *page_size_ptr = page_size;
+12 −6
Original line number Diff line number Diff line
@@ -8859,17 +8859,23 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
        case 0x08:
        case 0x09:
            {
                int j, n, user, loaded_base;
                int j, n, loaded_base;
                bool exc_return = false;
                bool is_load = extract32(insn, 20, 1);
                bool user = false;
                TCGv_i32 loaded_var;
                /* load/store multiple words */
                /* XXX: store correct base if write back */
                user = 0;
                if (insn & (1 << 22)) {
                    /* LDM (user), LDM (exception return) and STM (user) */
                    if (IS_USER(s))
                        goto illegal_op; /* only usable in supervisor mode */

                    if ((insn & (1 << 15)) == 0)
                        user = 1;
                    if (is_load && extract32(insn, 15, 1)) {
                        exc_return = true;
                    } else {
                        user = true;
                    }
                }
                rn = (insn >> 16) & 0xf;
                addr = load_reg(s, rn);
@@ -8903,7 +8909,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                j = 0;
                for(i=0;i<16;i++) {
                    if (insn & (1 << i)) {
                        if (insn & (1 << 20)) {
                        if (is_load) {
                            /* load */
                            tmp = tcg_temp_new_i32();
                            gen_aa32_ld32u(tmp, addr, get_mem_index(s));
@@ -8968,7 +8974,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                if (loaded_base) {
                    store_reg(s, rn, loaded_var);
                }
                if ((insn & (1 << 22)) && !user) {
                if (exc_return) {
                    /* Restore CPSR from SPSR.  */
                    tmp = load_cpu_field(spsr);
                    gen_set_cpsr(tmp, CPSR_ERET_MASK);