2 * x86 memory access helpers
4 * Copyright (c) 2003 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
22 #include "exec/helper-proto.h"
23 #include "exec/exec-all.h"
24 #include "exec/cpu_ldst.h"
25 #include "qemu/int128.h"
26 #include "qemu/atomic128.h"
29 void helper_cmpxchg8b_unlocked(CPUX86State
*env
, target_ulong a0
)
31 uintptr_t ra
= GETPC();
32 uint64_t oldv
, cmpv
, newv
;
35 eflags
= cpu_cc_compute_all(env
, CC_OP
);
37 cmpv
= deposit64(env
->regs
[R_EAX
], 32, 32, env
->regs
[R_EDX
]);
38 newv
= deposit64(env
->regs
[R_EBX
], 32, 32, env
->regs
[R_ECX
]);
40 oldv
= cpu_ldq_data_ra(env
, a0
, ra
);
41 newv
= (cmpv
== oldv
? newv
: oldv
);
42 /* always do the store */
43 cpu_stq_data_ra(env
, a0
, newv
, ra
);
48 env
->regs
[R_EAX
] = (uint32_t)oldv
;
49 env
->regs
[R_EDX
] = (uint32_t)(oldv
>> 32);
55 void helper_cmpxchg8b(CPUX86State
*env
, target_ulong a0
)
57 #ifdef CONFIG_ATOMIC64
58 uint64_t oldv
, cmpv
, newv
;
61 eflags
= cpu_cc_compute_all(env
, CC_OP
);
63 cmpv
= deposit64(env
->regs
[R_EAX
], 32, 32, env
->regs
[R_EDX
]);
64 newv
= deposit64(env
->regs
[R_EBX
], 32, 32, env
->regs
[R_ECX
]);
66 #ifdef CONFIG_USER_ONLY
68 uint64_t *haddr
= g2h(a0
);
69 cmpv
= cpu_to_le64(cmpv
);
70 newv
= cpu_to_le64(newv
);
71 oldv
= atomic_cmpxchg__nocheck(haddr
, cmpv
, newv
);
72 oldv
= le64_to_cpu(oldv
);
76 uintptr_t ra
= GETPC();
77 int mem_idx
= cpu_mmu_index(env
, false);
78 TCGMemOpIdx oi
= make_memop_idx(MO_TEQ
, mem_idx
);
79 oldv
= helper_atomic_cmpxchgq_le_mmu(env
, a0
, cmpv
, newv
, oi
, ra
);
86 env
->regs
[R_EAX
] = (uint32_t)oldv
;
87 env
->regs
[R_EDX
] = (uint32_t)(oldv
>> 32);
92 cpu_loop_exit_atomic(ENV_GET_CPU(env
), GETPC());
93 #endif /* CONFIG_ATOMIC64 */
97 void helper_cmpxchg16b_unlocked(CPUX86State
*env
, target_ulong a0
)
99 uintptr_t ra
= GETPC();
100 Int128 oldv
, cmpv
, newv
;
105 if ((a0
& 0xf) != 0) {
106 raise_exception_ra(env
, EXCP0D_GPF
, GETPC());
108 eflags
= cpu_cc_compute_all(env
, CC_OP
);
110 cmpv
= int128_make128(env
->regs
[R_EAX
], env
->regs
[R_EDX
]);
111 newv
= int128_make128(env
->regs
[R_EBX
], env
->regs
[R_ECX
]);
113 o0
= cpu_ldq_data_ra(env
, a0
+ 0, ra
);
114 o1
= cpu_ldq_data_ra(env
, a0
+ 8, ra
);
116 oldv
= int128_make128(o0
, o1
);
117 success
= int128_eq(oldv
, cmpv
);
122 cpu_stq_data_ra(env
, a0
+ 0, int128_getlo(newv
), ra
);
123 cpu_stq_data_ra(env
, a0
+ 8, int128_gethi(newv
), ra
);
128 env
->regs
[R_EAX
] = int128_getlo(oldv
);
129 env
->regs
[R_EDX
] = int128_gethi(oldv
);
135 void helper_cmpxchg16b(CPUX86State
*env
, target_ulong a0
)
137 uintptr_t ra
= GETPC();
139 if ((a0
& 0xf) != 0) {
140 raise_exception_ra(env
, EXCP0D_GPF
, ra
);
141 } else if (HAVE_CMPXCHG128
) {
142 int eflags
= cpu_cc_compute_all(env
, CC_OP
);
144 Int128 cmpv
= int128_make128(env
->regs
[R_EAX
], env
->regs
[R_EDX
]);
145 Int128 newv
= int128_make128(env
->regs
[R_EBX
], env
->regs
[R_ECX
]);
147 int mem_idx
= cpu_mmu_index(env
, false);
148 TCGMemOpIdx oi
= make_memop_idx(MO_TEQ
| MO_ALIGN_16
, mem_idx
);
149 Int128 oldv
= helper_atomic_cmpxchgo_le_mmu(env
, a0
, cmpv
,
152 if (int128_eq(oldv
, cmpv
)) {
155 env
->regs
[R_EAX
] = int128_getlo(oldv
);
156 env
->regs
[R_EDX
] = int128_gethi(oldv
);
161 cpu_loop_exit_atomic(ENV_GET_CPU(env
), ra
);
166 void helper_boundw(CPUX86State
*env
, target_ulong a0
, int v
)
170 low
= cpu_ldsw_data_ra(env
, a0
, GETPC());
171 high
= cpu_ldsw_data_ra(env
, a0
+ 2, GETPC());
173 if (v
< low
|| v
> high
) {
174 if (env
->hflags
& HF_MPX_EN_MASK
) {
175 env
->bndcs_regs
.sts
= 0;
177 raise_exception_ra(env
, EXCP05_BOUND
, GETPC());
181 void helper_boundl(CPUX86State
*env
, target_ulong a0
, int v
)
185 low
= cpu_ldl_data_ra(env
, a0
, GETPC());
186 high
= cpu_ldl_data_ra(env
, a0
+ 4, GETPC());
187 if (v
< low
|| v
> high
) {
188 if (env
->hflags
& HF_MPX_EN_MASK
) {
189 env
->bndcs_regs
.sts
= 0;
191 raise_exception_ra(env
, EXCP05_BOUND
, GETPC());
195 #if !defined(CONFIG_USER_ONLY)
196 /* try to fill the TLB and return an exception if error. If retaddr is
197 * NULL, it means that the function was called in C code (i.e. not
198 * from generated code or from helper.c)
200 /* XXX: fix it to restore all registers */
201 void tlb_fill(CPUState
*cs
, target_ulong addr
, int size
,
202 MMUAccessType access_type
, int mmu_idx
, uintptr_t retaddr
)
204 X86CPU
*cpu
= X86_CPU(cs
);
205 CPUX86State
*env
= &cpu
->env
;
208 env
->retaddr
= retaddr
;
209 ret
= x86_cpu_handle_mmu_fault(cs
, addr
, size
, access_type
, mmu_idx
);
211 raise_exception_err_ra(env
, cs
->exception_index
, env
->error_code
, retaddr
);