| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  x86 memory access helpers | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (c) 2003 Fabrice Bellard | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This library is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU Lesser General Public | 
					
						
							|  |  |  |  * License as published by the Free Software Foundation; either | 
					
						
							| 
									
										
										
										
											2020-10-23 12:28:01 +00:00
										 |  |  |  * version 2.1 of the License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  * Lesser General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Lesser General Public | 
					
						
							|  |  |  |  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:03 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | #include "cpu.h"
 | 
					
						
							| 
									
										
										
										
											2014-04-07 22:31:41 -07:00
										 |  |  | #include "exec/helper-proto.h"
 | 
					
						
							| 
									
										
										
										
											2016-03-15 13:18:37 +01:00
										 |  |  | #include "exec/exec-all.h"
 | 
					
						
							| 
									
										
										
										
											2014-03-28 19:42:10 +01:00
										 |  |  | #include "exec/cpu_ldst.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | #include "qemu/int128.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-15 16:47:59 -07:00
										 |  |  | #include "qemu/atomic128.h"
 | 
					
						
							| 
									
										
										
										
											2020-01-01 12:23:00 +01:00
										 |  |  | #include "tcg/tcg.h"
 | 
					
						
							| 
									
										
										
										
											2020-12-12 16:55:14 +01:00
										 |  |  | #include "helper-tcg.h"
 | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uintptr_t ra = GETPC(); | 
					
						
							|  |  |  |     uint64_t oldv, cmpv, newv; | 
					
						
							|  |  |  |     int eflags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     eflags = cpu_cc_compute_all(env, CC_OP); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); | 
					
						
							|  |  |  |     newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oldv = cpu_ldq_data_ra(env, a0, ra); | 
					
						
							|  |  |  |     newv = (cmpv == oldv ? newv : oldv); | 
					
						
							|  |  |  |     /* always do the store */ | 
					
						
							|  |  |  |     cpu_stq_data_ra(env, a0, newv, ra); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (oldv == cmpv) { | 
					
						
							|  |  |  |         eflags |= CC_Z; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         env->regs[R_EAX] = (uint32_t)oldv; | 
					
						
							|  |  |  |         env->regs[R_EDX] = (uint32_t)(oldv >> 32); | 
					
						
							|  |  |  |         eflags &= ~CC_Z; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     CC_SRC = eflags; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-29 20:35:48 +00:00
										 |  |  | void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | #ifdef CONFIG_ATOMIC64
 | 
					
						
							|  |  |  |     uint64_t oldv, cmpv, newv; | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     int eflags; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-29 12:45:34 +00:00
										 |  |  |     eflags = cpu_cc_compute_all(env, CC_OP); | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); | 
					
						
							|  |  |  |     newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_USER_ONLY
 | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-02-12 10:48:43 -08:00
										 |  |  |         uint64_t *haddr = g2h(env_cpu(env), a0); | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |         cmpv = cpu_to_le64(cmpv); | 
					
						
							|  |  |  |         newv = cpu_to_le64(newv); | 
					
						
							| 
									
										
										
										
											2020-09-23 11:56:46 +01:00
										 |  |  |         oldv = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |         oldv = le64_to_cpu(oldv); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         uintptr_t ra = GETPC(); | 
					
						
							|  |  |  |         int mem_idx = cpu_mmu_index(env, false); | 
					
						
							|  |  |  |         TCGMemOpIdx oi = make_memop_idx(MO_TEQ, mem_idx); | 
					
						
							|  |  |  |         oldv = helper_atomic_cmpxchgq_le_mmu(env, a0, cmpv, newv, oi, ra); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (oldv == cmpv) { | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |         eflags |= CC_Z; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |         env->regs[R_EAX] = (uint32_t)oldv; | 
					
						
							|  |  |  |         env->regs[R_EDX] = (uint32_t)(oldv >> 32); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |         eflags &= ~CC_Z; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     CC_SRC = eflags; | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2019-03-22 16:07:18 -07:00
										 |  |  |     cpu_loop_exit_atomic(env_cpu(env), GETPC()); | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | #endif /* CONFIG_ATOMIC64 */
 | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef TARGET_X86_64
 | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | void helper_cmpxchg16b_unlocked(CPUX86State *env, target_ulong a0) | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |     uintptr_t ra = GETPC(); | 
					
						
							|  |  |  |     Int128 oldv, cmpv, newv; | 
					
						
							|  |  |  |     uint64_t o0, o1; | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     int eflags; | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |     bool success; | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((a0 & 0xf) != 0) { | 
					
						
							| 
									
										
										
										
											2015-07-10 12:57:30 +03:00
										 |  |  |         raise_exception_ra(env, EXCP0D_GPF, GETPC()); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-04-29 12:45:34 +00:00
										 |  |  |     eflags = cpu_cc_compute_all(env, CC_OP); | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); | 
					
						
							|  |  |  |     newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     o0 = cpu_ldq_data_ra(env, a0 + 0, ra); | 
					
						
							|  |  |  |     o1 = cpu_ldq_data_ra(env, a0 + 8, ra); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oldv = int128_make128(o0, o1); | 
					
						
							|  |  |  |     success = int128_eq(oldv, cmpv); | 
					
						
							|  |  |  |     if (!success) { | 
					
						
							|  |  |  |         newv = oldv; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cpu_stq_data_ra(env, a0 + 0, int128_getlo(newv), ra); | 
					
						
							|  |  |  |     cpu_stq_data_ra(env, a0 + 8, int128_gethi(newv), ra); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (success) { | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |         eflags |= CC_Z; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |         env->regs[R_EAX] = int128_getlo(oldv); | 
					
						
							|  |  |  |         env->regs[R_EDX] = int128_gethi(oldv); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |         eflags &= ~CC_Z; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     CC_SRC = eflags; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uintptr_t ra = GETPC(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((a0 & 0xf) != 0) { | 
					
						
							|  |  |  |         raise_exception_ra(env, EXCP0D_GPF, ra); | 
					
						
							| 
									
										
										
										
											2018-08-15 16:47:59 -07:00
										 |  |  |     } else if (HAVE_CMPXCHG128) { | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |         int eflags = cpu_cc_compute_all(env, CC_OP); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); | 
					
						
							|  |  |  |         Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int mem_idx = cpu_mmu_index(env, false); | 
					
						
							|  |  |  |         TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); | 
					
						
							|  |  |  |         Int128 oldv = helper_atomic_cmpxchgo_le_mmu(env, a0, cmpv, | 
					
						
							|  |  |  |                                                     newv, oi, ra); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (int128_eq(oldv, cmpv)) { | 
					
						
							|  |  |  |             eflags |= CC_Z; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             env->regs[R_EAX] = int128_getlo(oldv); | 
					
						
							|  |  |  |             env->regs[R_EDX] = int128_gethi(oldv); | 
					
						
							|  |  |  |             eflags &= ~CC_Z; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         CC_SRC = eflags; | 
					
						
							| 
									
										
										
										
											2018-08-15 16:47:59 -07:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2019-03-22 16:07:18 -07:00
										 |  |  |         cpu_loop_exit_atomic(env_cpu(env), ra); | 
					
						
							| 
									
										
										
										
											2016-06-27 15:01:51 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-29 20:35:48 +00:00
										 |  |  | void helper_boundw(CPUX86State *env, target_ulong a0, int v) | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     int low, high; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-10 12:57:30 +03:00
										 |  |  |     low = cpu_ldsw_data_ra(env, a0, GETPC()); | 
					
						
							|  |  |  |     high = cpu_ldsw_data_ra(env, a0 + 2, GETPC()); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     v = (int16_t)v; | 
					
						
							|  |  |  |     if (v < low || v > high) { | 
					
						
							| 
									
										
										
										
											2015-07-06 19:37:40 +01:00
										 |  |  |         if (env->hflags & HF_MPX_EN_MASK) { | 
					
						
							|  |  |  |             env->bndcs_regs.sts = 0; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-07-10 12:57:30 +03:00
										 |  |  |         raise_exception_ra(env, EXCP05_BOUND, GETPC()); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-29 20:35:48 +00:00
										 |  |  | void helper_boundl(CPUX86State *env, target_ulong a0, int v) | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     int low, high; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-10 12:57:30 +03:00
										 |  |  |     low = cpu_ldl_data_ra(env, a0, GETPC()); | 
					
						
							|  |  |  |     high = cpu_ldl_data_ra(env, a0 + 4, GETPC()); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     if (v < low || v > high) { | 
					
						
							| 
									
										
										
										
											2015-07-06 19:37:40 +01:00
										 |  |  |         if (env->hflags & HF_MPX_EN_MASK) { | 
					
						
							|  |  |  |             env->bndcs_regs.sts = 0; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-07-10 12:57:30 +03:00
										 |  |  |         raise_exception_ra(env, EXCP05_BOUND, GETPC()); | 
					
						
							| 
									
										
										
										
											2012-04-29 16:39:13 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } |