diff options
-rw-r--r-- | fsdev/virtfs-proxy-helper.c | 4 | ||||
-rw-r--r-- | hw/9pfs/virtio-9p-local.c | 52 | ||||
-rw-r--r-- | hw/9pfs/virtio-9p-posix-acl.c | 2 | ||||
-rw-r--r-- | hw/9pfs/virtio-9p-proxy.c | 22 | ||||
-rw-r--r-- | hw/intc/arm_gic_kvm.c | 7 | ||||
-rw-r--r-- | linux-user/arm/target_cpu.h | 15 | ||||
-rw-r--r-- | linux-user/main.c | 2 | ||||
-rw-r--r-- | target-arm/helper.c | 222 | ||||
-rw-r--r-- | target-arm/translate.c | 18 |
9 files changed, 233 insertions, 111 deletions
diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index c1da2d7..13fe032 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -262,6 +262,9 @@ static int send_status(int sockfd, struct iovec *iovec, int status) */ msg_size = proxy_marshal(iovec, 0, "ddd", header.type, header.size, status); + if (msg_size < 0) { + return msg_size; + } retval = socket_write(sockfd, iovec->iov_base, msg_size); if (retval < 0) { return retval; @@ -735,6 +738,7 @@ static int proxy_socket(const char *path, uid_t uid, gid_t gid) return -1; } + g_assert(strlen(path) < sizeof(proxy.sun_path)); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { do_perror("socket"); diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index d05c917..d6b1c0c 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -45,19 +45,17 @@ static char *local_mapped_attr_path(FsContext *ctx, const char *path) { - char *dir_name; - char *tmp_path = g_strdup(path); - char *base_name = basename(tmp_path); - char *buffer; - - /* NULL terminate the directory */ - dir_name = tmp_path; - *(base_name - 1) = '\0'; - - buffer = g_strdup_printf("%s/%s/%s/%s", - ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name); - g_free(tmp_path); - return buffer; + int dirlen; + const char *name = strrchr(path, '/'); + if (name) { + dirlen = name - path; + ++name; + } else { + name = path; + dirlen = 0; + } + return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root, + dirlen, path, VIRTFS_META_DIR, name); } static FILE *local_fopen(const char *path, const char *mode) @@ -488,7 +486,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, int err = -1; int serrno = 0; V9fsString fullname; - char *buffer; + char *buffer = NULL; v9fs_string_init(&fullname); v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); @@ -499,7 +497,6 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); if (err == -1) { - g_free(buffer); goto out; } err = local_set_xattr(buffer, credp); @@ -512,7 +509,6 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); if (err == -1) { - g_free(buffer); goto out; } err = local_set_mapped_file_attr(fs_ctx, path, credp); @@ -525,7 +521,6 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mknod(buffer, credp->fc_mode, credp->fc_rdev); if (err == -1) { - g_free(buffer); goto out; } err = local_post_create_passthrough(fs_ctx, path, credp); @@ -539,8 +534,8 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, err_end: remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } @@ -552,7 +547,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, int err = -1; int serrno = 0; V9fsString fullname; - char *buffer; + char *buffer = NULL; v9fs_string_init(&fullname); v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); @@ -563,7 +558,6 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); if (err == -1) { - g_free(buffer); goto out; } credp->fc_mode = credp->fc_mode|S_IFDIR; @@ -576,7 +570,6 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); if (err == -1) { - g_free(buffer); goto out; } credp->fc_mode = credp->fc_mode|S_IFDIR; @@ -590,7 +583,6 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mkdir(buffer, credp->fc_mode); if (err == -1) { - g_free(buffer); goto out; } err = local_post_create_passthrough(fs_ctx, path, credp); @@ -604,8 +596,8 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, err_end: remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } @@ -659,7 +651,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, int err = -1; int serrno = 0; V9fsString fullname; - char *buffer; + char *buffer = NULL; /* * Mark all the open to not follow symlinks @@ -675,7 +667,6 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, buffer = rpath(fs_ctx, path); fd = open(buffer, flags, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -690,7 +681,6 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, buffer = rpath(fs_ctx, path); fd = open(buffer, flags, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -706,7 +696,6 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, buffer = rpath(fs_ctx, path); fd = open(buffer, flags, credp->fc_mode); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -724,8 +713,8 @@ err_end: close(fd); remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } @@ -738,7 +727,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, int serrno = 0; char *newpath; V9fsString fullname; - char *buffer; + char *buffer = NULL; v9fs_string_init(&fullname); v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); @@ -751,7 +740,6 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, buffer = rpath(fs_ctx, newpath); fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -781,7 +769,6 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, buffer = rpath(fs_ctx, newpath); fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -810,7 +797,6 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, buffer = rpath(fs_ctx, newpath); err = symlink(oldpath, buffer); if (err) { - g_free(buffer); goto out; } err = lchown(buffer, credp->fc_uid, credp->fc_gid); @@ -831,8 +817,8 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, err_end: remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c index 803d9d9..09dad07 100644 --- a/hw/9pfs/virtio-9p-posix-acl.c +++ b/hw/9pfs/virtio-9p-posix-acl.c @@ -114,7 +114,7 @@ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, } /* len includes the trailing NUL */ - memcpy(value, ACL_ACCESS, len); + memcpy(value, ACL_DEFAULT, len); return 0; } diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c index 59c7445..71b6198 100644 --- a/hw/9pfs/virtio-9p-proxy.c +++ b/hw/9pfs/virtio-9p-proxy.c @@ -693,16 +693,16 @@ static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, const struct iovec *iov, int iovcnt, off_t offset) { + ssize_t ret; #ifdef CONFIG_PREADV - return preadv(fs->fd, iov, iovcnt, offset); + ret = preadv(fs->fd, iov, iovcnt, offset); #else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - return readv(fs->fd, iov, iovcnt); + ret = lseek(fs->fd, offset, SEEK_SET); + if (ret >= 0) { + ret = readv(fs->fd, iov, iovcnt); } #endif + return ret; } static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, @@ -714,10 +714,8 @@ static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, #ifdef CONFIG_PREADV ret = pwritev(fs->fd, iov, iovcnt, offset); #else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { + ret = lseek(fs->fd, offset, SEEK_SET); + if (ret >= 0) { ret = writev(fs->fd, iov, iovcnt); } #endif @@ -1102,6 +1100,10 @@ static int connect_namedsocket(const char *path) int sockfd, size; struct sockaddr_un helper; + if (strlen(path) >= sizeof(helper.sun_path)) { + fprintf(stderr, "Socket name too large\n"); + return -1; + } sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { fprintf(stderr, "failed to create socket: %s\n", strerror(errno)); diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 1ad3eb0..0d20750 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -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); diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index d8a534d..6832262 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -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) { - env->cp15.tpidrro_el[0] = 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 diff --git a/linux-user/main.c b/linux-user/main.c index 6bd23af..6e446de 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -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); diff --git a/target-arm/helper.c b/target-arm/helper.c index 3bc20af..10886c5 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -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; - /* The simplified model uses AP[0] as an access control bit. */ - if ((regime_sctlr(env, mmu_idx) & SCTLR_AFE) - && (ap & 1) == 0) { - /* Access flag fault. */ - code = (code == 15) ? 6 : 3; - 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 ((ap & 1) == 0) { + /* Access flag fault. */ + code = (code == 15) ? 6 : 3; + 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 (*prot && !xn) { + *prot |= PAGE_EXEC; } - *prot = check_ap(env, mmu_idx, ap, domain_prot, access_type); - if (!*prot) { + if (!(*prot & (1 << access_type))) { /* Access permission fault. */ goto do_fault; } - if (!xn) { - *prot |= PAGE_EXEC; - } } *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 */ + if (!(*prot & (1 << access_type))) { 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) { - goto do_fault; - } - *prot &= ~PAGE_WRITE; - } *phys_ptr = descaddr; *page_size_ptr = page_size; diff --git a/target-arm/translate.c b/target-arm/translate.c index 381d896..9116529 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -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); |