VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IEMAllCImplStrInstr.cpp.h@ 80089

Last change on this file since 80089 was 80089, checked in by vboxsync, 5 years ago

VMM: Kicking out raw-mode - IEM. bugref:9517

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.7 KB
Line 
1/* $Id: IEMAllCImplStrInstr.cpp.h 80089 2019-07-31 20:50:22Z vboxsync $ */
2/** @file
3 * IEM - String Instruction Implementation Code Template.
4 */
5
6/*
7 * Copyright (C) 2011-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Defined Constants And Macros *
21*******************************************************************************/
22#if OP_SIZE == 8
23# define OP_rAX al
24#elif OP_SIZE == 16
25# define OP_rAX ax
26#elif OP_SIZE == 32
27# define OP_rAX eax
28#elif OP_SIZE == 64
29# define OP_rAX rax
30#else
31# error "Bad OP_SIZE."
32#endif
33#define OP_TYPE RT_CONCAT3(uint,OP_SIZE,_t)
34
35#if ADDR_SIZE == 16
36# define ADDR_rDI di
37# define ADDR_rSI si
38# define ADDR_rCX cx
39# define ADDR2_TYPE uint32_t
40# define ADDR_VMXSTRIO 0
41#elif ADDR_SIZE == 32
42# define ADDR_rDI edi
43# define ADDR_rSI esi
44# define ADDR_rCX ecx
45# define ADDR2_TYPE uint32_t
46# define ADDR_VMXSTRIO 1
47#elif ADDR_SIZE == 64
48# define ADDR_rDI rdi
49# define ADDR_rSI rsi
50# define ADDR_rCX rcx
51# define ADDR2_TYPE uint64_t
52# define ADDR_VMXSTRIO 2
53# define IS_64_BIT_CODE(a_pVCpu) (true)
54#else
55# error "Bad ADDR_SIZE."
56#endif
57#define ADDR_TYPE RT_CONCAT3(uint,ADDR_SIZE,_t)
58
59#if ADDR_SIZE == 64 || OP_SIZE == 64
60# define IS_64_BIT_CODE(a_pVCpu) (true)
61#elif ADDR_SIZE == 32
62# define IS_64_BIT_CODE(a_pVCpu) ((a_pVCpu)->iem.s.enmCpuMode == IEMMODE_64BIT)
63#else
64# define IS_64_BIT_CODE(a_pVCpu) (false)
65#endif
66
67/** @def IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN
68 * Used in the outer (page-by-page) loop to check for reasons for returnning
69 * before completing the instruction. In raw-mode we temporarily enable
70 * interrupts to let the host interrupt us. We cannot let big string operations
71 * hog the CPU, especially not in raw-mode.
72 */
73#define IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(a_pVM, a_pVCpu, a_fEflags) \
74 do { \
75 if (RT_LIKELY( !VMCPU_FF_IS_ANY_SET(a_pVCpu, (a_fEflags) & X86_EFL_IF ? VMCPU_FF_YIELD_REPSTR_MASK \
76 : VMCPU_FF_YIELD_REPSTR_NOINT_MASK) \
77 && !VM_FF_IS_ANY_SET(a_pVM, VM_FF_YIELD_REPSTR_MASK) \
78 )) \
79 { /* probable */ } \
80 else \
81 { \
82 LogFlow(("%s: Leaving early (outer)! ffcpu=%#RX64 ffvm=%#x\n", \
83 __FUNCTION__, (uint64_t)(a_pVCpu)->fLocalForcedActions, (a_pVM)->fGlobalForcedActions)); \
84 return VINF_SUCCESS; \
85 } \
86 } while (0)
87
88/** @def IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN
89 * This is used in some of the inner loops to make sure we respond immediately
90 * to VMCPU_FF_IOM as well as outside requests. Use this for expensive
91 * instructions. Use IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN for
92 * ones that are typically cheap. */
93#define IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(a_pVM, a_pVCpu, a_fExitExpr) \
94 do { \
95 if (RT_LIKELY( ( !VMCPU_FF_IS_ANY_SET(a_pVCpu, VMCPU_FF_HIGH_PRIORITY_POST_REPSTR_MASK) \
96 && !VM_FF_IS_ANY_SET(a_pVM, VM_FF_HIGH_PRIORITY_POST_REPSTR_MASK)) \
97 || (a_fExitExpr) )) \
98 { /* very likely */ } \
99 else \
100 { \
101 LogFlow(("%s: Leaving early (inner)! ffcpu=%#RX64 ffvm=%#x\n", \
102 __FUNCTION__, (uint64_t)(a_pVCpu)->fLocalForcedActions, (a_pVM)->fGlobalForcedActions)); \
103 return VINF_SUCCESS; \
104 } \
105 } while (0)
106
107
108/** @def IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN
109 * This is used in the inner loops where
110 * IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN isn't used. It only
111 * checks the CPU FFs so that we respond immediately to the pending IOM FF
112 * (status code is hidden in IEMCPU::rcPassUp by IEM memory commit code).
113 */
114#define IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(a_pVM, a_pVCpu, a_fExitExpr) \
115 do { \
116 if (RT_LIKELY( !VMCPU_FF_IS_ANY_SET(a_pVCpu, VMCPU_FF_HIGH_PRIORITY_POST_REPSTR_MASK) \
117 || (a_fExitExpr) )) \
118 { /* very likely */ } \
119 else \
120 { \
121 LogFlow(("%s: Leaving early (inner)! ffcpu=%#RX64 (ffvm=%#x)\n", \
122 __FUNCTION__, (uint64_t)(a_pVCpu)->fLocalForcedActions, (a_pVM)->fGlobalForcedActions)); \
123 return VINF_SUCCESS; \
124 } \
125 } while (0)
126
127
128/**
129 * Implements 'REPE CMPS'.
130 */
131IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_repe_cmps_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg)
132{
133 PVM pVM = pVCpu->CTX_SUFF(pVM);
134
135 /*
136 * Setup.
137 */
138 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
139 if (uCounterReg == 0)
140 {
141 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
142 return VINF_SUCCESS;
143 }
144
145 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_FROM_IDX(iEffSeg) | CPUMCTX_EXTRN_ES);
146
147 PCCPUMSELREGHID pSrc1Hid = iemSRegGetHid(pVCpu, iEffSeg);
148 uint64_t uSrc1Base;
149 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pSrc1Hid, iEffSeg, &uSrc1Base);
150 if (rcStrict != VINF_SUCCESS)
151 return rcStrict;
152
153 uint64_t uSrc2Base;
154 rcStrict = iemMemSegCheckReadAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uSrc2Base);
155 if (rcStrict != VINF_SUCCESS)
156 return rcStrict;
157
158 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
159 ADDR_TYPE uSrc1AddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
160 ADDR_TYPE uSrc2AddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
161 uint32_t uEFlags = pVCpu->cpum.GstCtx.eflags.u;
162
163 /*
164 * The loop.
165 */
166 for (;;)
167 {
168 /*
169 * Do segmentation and virtual page stuff.
170 */
171 ADDR2_TYPE uVirtSrc1Addr = uSrc1AddrReg + (ADDR2_TYPE)uSrc1Base;
172 ADDR2_TYPE uVirtSrc2Addr = uSrc2AddrReg + (ADDR2_TYPE)uSrc2Base;
173 uint32_t cLeftSrc1Page = (PAGE_SIZE - (uVirtSrc1Addr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
174 if (cLeftSrc1Page > uCounterReg)
175 cLeftSrc1Page = uCounterReg;
176 uint32_t cLeftSrc2Page = (PAGE_SIZE - (uVirtSrc2Addr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
177 uint32_t cLeftPage = RT_MIN(cLeftSrc1Page, cLeftSrc2Page);
178
179 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
180 && cbIncr > 0 /** @todo Optimize reverse direction string ops. */
181 && ( IS_64_BIT_CODE(pVCpu)
182 || ( uSrc1AddrReg < pSrc1Hid->u32Limit
183 && uSrc1AddrReg + (cLeftPage * (OP_SIZE / 8)) <= pSrc1Hid->u32Limit
184 && uSrc2AddrReg < pVCpu->cpum.GstCtx.es.u32Limit
185 && uSrc2AddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
186 )
187 )
188 {
189 RTGCPHYS GCPhysSrc1Mem;
190 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtSrc1Addr, IEM_ACCESS_DATA_R, &GCPhysSrc1Mem);
191 if (rcStrict != VINF_SUCCESS)
192 return rcStrict;
193
194 RTGCPHYS GCPhysSrc2Mem;
195 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtSrc2Addr, IEM_ACCESS_DATA_R, &GCPhysSrc2Mem);
196 if (rcStrict != VINF_SUCCESS)
197 return rcStrict;
198
199 /*
200 * If we can map the page without trouble, do a block processing
201 * until the end of the current page.
202 */
203 PGMPAGEMAPLOCK PgLockSrc2Mem;
204 OP_TYPE const *puSrc2Mem;
205 rcStrict = iemMemPageMap(pVCpu, GCPhysSrc2Mem, IEM_ACCESS_DATA_R, (void **)&puSrc2Mem, &PgLockSrc2Mem);
206 if (rcStrict == VINF_SUCCESS)
207 {
208 PGMPAGEMAPLOCK PgLockSrc1Mem;
209 OP_TYPE const *puSrc1Mem;
210 rcStrict = iemMemPageMap(pVCpu, GCPhysSrc1Mem, IEM_ACCESS_DATA_R, (void **)&puSrc1Mem, &PgLockSrc1Mem);
211 if (rcStrict == VINF_SUCCESS)
212 {
213 if (!memcmp(puSrc2Mem, puSrc1Mem, cLeftPage * (OP_SIZE / 8)))
214 {
215 /* All matches, only compare the last itme to get the right eflags. */
216 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&puSrc1Mem[cLeftPage-1], puSrc2Mem[cLeftPage-1], &uEFlags);
217 uSrc1AddrReg += cLeftPage * cbIncr;
218 uSrc2AddrReg += cLeftPage * cbIncr;
219 uCounterReg -= cLeftPage;
220 }
221 else
222 {
223 /* Some mismatch, compare each item (and keep volatile
224 memory in mind). */
225 uint32_t off = 0;
226 do
227 {
228 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&puSrc1Mem[off], puSrc2Mem[off], &uEFlags);
229 off++;
230 } while ( off < cLeftPage
231 && (uEFlags & X86_EFL_ZF));
232 uSrc1AddrReg += cbIncr * off;
233 uSrc2AddrReg += cbIncr * off;
234 uCounterReg -= off;
235 }
236
237 /* Update the registers before looping. */
238 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg;
239 pVCpu->cpum.GstCtx.ADDR_rSI = uSrc1AddrReg;
240 pVCpu->cpum.GstCtx.ADDR_rDI = uSrc2AddrReg;
241 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
242
243 iemMemPageUnmap(pVCpu, GCPhysSrc1Mem, IEM_ACCESS_DATA_R, puSrc1Mem, &PgLockSrc1Mem);
244 iemMemPageUnmap(pVCpu, GCPhysSrc2Mem, IEM_ACCESS_DATA_R, puSrc2Mem, &PgLockSrc2Mem);
245 if ( uCounterReg == 0
246 || !(uEFlags & X86_EFL_ZF))
247 break;
248 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
249 continue;
250 }
251 iemMemPageUnmap(pVCpu, GCPhysSrc2Mem, IEM_ACCESS_DATA_R, puSrc2Mem, &PgLockSrc2Mem);
252 }
253 }
254
255 /*
256 * Fallback - slow processing till the end of the current page.
257 * In the cross page boundrary case we will end up here with cLeftPage
258 * as 0, we execute one loop then.
259 */
260 do
261 {
262 OP_TYPE uValue1;
263 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue1, iEffSeg, uSrc1AddrReg);
264 if (rcStrict != VINF_SUCCESS)
265 return rcStrict;
266 OP_TYPE uValue2;
267 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue2, X86_SREG_ES, uSrc2AddrReg);
268 if (rcStrict != VINF_SUCCESS)
269 return rcStrict;
270 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)(&uValue1, uValue2, &uEFlags);
271
272 pVCpu->cpum.GstCtx.ADDR_rSI = uSrc1AddrReg += cbIncr;
273 pVCpu->cpum.GstCtx.ADDR_rDI = uSrc2AddrReg += cbIncr;
274 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
275 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
276 cLeftPage--;
277 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0 || !(uEFlags & X86_EFL_ZF));
278 } while ( (int32_t)cLeftPage > 0
279 && (uEFlags & X86_EFL_ZF));
280
281 /*
282 * Next page? Must check for interrupts and stuff here.
283 */
284 if ( uCounterReg == 0
285 || !(uEFlags & X86_EFL_ZF))
286 break;
287 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
288 }
289
290 /*
291 * Done.
292 */
293 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
294 return VINF_SUCCESS;
295}
296
297
298/**
299 * Implements 'REPNE CMPS'.
300 */
301IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_repne_cmps_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg)
302{
303 PVM pVM = pVCpu->CTX_SUFF(pVM);
304
305 /*
306 * Setup.
307 */
308 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
309 if (uCounterReg == 0)
310 {
311 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
312 return VINF_SUCCESS;
313 }
314
315 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_FROM_IDX(iEffSeg) | CPUMCTX_EXTRN_ES);
316
317 PCCPUMSELREGHID pSrc1Hid = iemSRegGetHid(pVCpu, iEffSeg);
318 uint64_t uSrc1Base;
319 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pSrc1Hid, iEffSeg, &uSrc1Base);
320 if (rcStrict != VINF_SUCCESS)
321 return rcStrict;
322
323 uint64_t uSrc2Base;
324 rcStrict = iemMemSegCheckReadAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uSrc2Base);
325 if (rcStrict != VINF_SUCCESS)
326 return rcStrict;
327
328 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
329 ADDR_TYPE uSrc1AddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
330 ADDR_TYPE uSrc2AddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
331 uint32_t uEFlags = pVCpu->cpum.GstCtx.eflags.u;
332
333 /*
334 * The loop.
335 */
336 for (;;)
337 {
338 /*
339 * Do segmentation and virtual page stuff.
340 */
341 ADDR2_TYPE uVirtSrc1Addr = uSrc1AddrReg + (ADDR2_TYPE)uSrc1Base;
342 ADDR2_TYPE uVirtSrc2Addr = uSrc2AddrReg + (ADDR2_TYPE)uSrc2Base;
343 uint32_t cLeftSrc1Page = (PAGE_SIZE - (uVirtSrc1Addr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
344 if (cLeftSrc1Page > uCounterReg)
345 cLeftSrc1Page = uCounterReg;
346 uint32_t cLeftSrc2Page = (PAGE_SIZE - (uVirtSrc2Addr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
347 uint32_t cLeftPage = RT_MIN(cLeftSrc1Page, cLeftSrc2Page);
348
349 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
350 && cbIncr > 0 /** @todo Optimize reverse direction string ops. */
351 && ( IS_64_BIT_CODE(pVCpu)
352 || ( uSrc1AddrReg < pSrc1Hid->u32Limit
353 && uSrc1AddrReg + (cLeftPage * (OP_SIZE / 8)) <= pSrc1Hid->u32Limit
354 && uSrc2AddrReg < pVCpu->cpum.GstCtx.es.u32Limit
355 && uSrc2AddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
356 )
357 )
358 {
359 RTGCPHYS GCPhysSrc1Mem;
360 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtSrc1Addr, IEM_ACCESS_DATA_R, &GCPhysSrc1Mem);
361 if (rcStrict != VINF_SUCCESS)
362 return rcStrict;
363
364 RTGCPHYS GCPhysSrc2Mem;
365 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtSrc2Addr, IEM_ACCESS_DATA_R, &GCPhysSrc2Mem);
366 if (rcStrict != VINF_SUCCESS)
367 return rcStrict;
368
369 /*
370 * If we can map the page without trouble, do a block processing
371 * until the end of the current page.
372 */
373 OP_TYPE const *puSrc2Mem;
374 PGMPAGEMAPLOCK PgLockSrc2Mem;
375 rcStrict = iemMemPageMap(pVCpu, GCPhysSrc2Mem, IEM_ACCESS_DATA_R, (void **)&puSrc2Mem, &PgLockSrc2Mem);
376 if (rcStrict == VINF_SUCCESS)
377 {
378 OP_TYPE const *puSrc1Mem;
379 PGMPAGEMAPLOCK PgLockSrc1Mem;
380 rcStrict = iemMemPageMap(pVCpu, GCPhysSrc1Mem, IEM_ACCESS_DATA_R, (void **)&puSrc1Mem, &PgLockSrc1Mem);
381 if (rcStrict == VINF_SUCCESS)
382 {
383 if (memcmp(puSrc2Mem, puSrc1Mem, cLeftPage * (OP_SIZE / 8)))
384 {
385 /* All matches, only compare the last item to get the right eflags. */
386 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&puSrc1Mem[cLeftPage-1], puSrc2Mem[cLeftPage-1], &uEFlags);
387 uSrc1AddrReg += cLeftPage * cbIncr;
388 uSrc2AddrReg += cLeftPage * cbIncr;
389 uCounterReg -= cLeftPage;
390 }
391 else
392 {
393 /* Some mismatch, compare each item (and keep volatile
394 memory in mind). */
395 uint32_t off = 0;
396 do
397 {
398 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&puSrc1Mem[off], puSrc2Mem[off], &uEFlags);
399 off++;
400 } while ( off < cLeftPage
401 && !(uEFlags & X86_EFL_ZF));
402 uSrc1AddrReg += cbIncr * off;
403 uSrc2AddrReg += cbIncr * off;
404 uCounterReg -= off;
405 }
406
407 /* Update the registers before looping. */
408 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg;
409 pVCpu->cpum.GstCtx.ADDR_rSI = uSrc1AddrReg;
410 pVCpu->cpum.GstCtx.ADDR_rDI = uSrc2AddrReg;
411 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
412
413 iemMemPageUnmap(pVCpu, GCPhysSrc1Mem, IEM_ACCESS_DATA_R, puSrc1Mem, &PgLockSrc1Mem);
414 iemMemPageUnmap(pVCpu, GCPhysSrc2Mem, IEM_ACCESS_DATA_R, puSrc2Mem, &PgLockSrc2Mem);
415 if ( uCounterReg == 0
416 || (uEFlags & X86_EFL_ZF))
417 break;
418 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
419 continue;
420 }
421 iemMemPageUnmap(pVCpu, GCPhysSrc2Mem, IEM_ACCESS_DATA_R, puSrc2Mem, &PgLockSrc2Mem);
422 }
423 }
424
425 /*
426 * Fallback - slow processing till the end of the current page.
427 * In the cross page boundrary case we will end up here with cLeftPage
428 * as 0, we execute one loop then.
429 */
430 do
431 {
432 OP_TYPE uValue1;
433 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue1, iEffSeg, uSrc1AddrReg);
434 if (rcStrict != VINF_SUCCESS)
435 return rcStrict;
436 OP_TYPE uValue2;
437 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue2, X86_SREG_ES, uSrc2AddrReg);
438 if (rcStrict != VINF_SUCCESS)
439 return rcStrict;
440 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)(&uValue1, uValue2, &uEFlags);
441
442 pVCpu->cpum.GstCtx.ADDR_rSI = uSrc1AddrReg += cbIncr;
443 pVCpu->cpum.GstCtx.ADDR_rDI = uSrc2AddrReg += cbIncr;
444 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
445 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
446 cLeftPage--;
447 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0 || (uEFlags & X86_EFL_ZF));
448 } while ( (int32_t)cLeftPage > 0
449 && !(uEFlags & X86_EFL_ZF));
450
451 /*
452 * Next page? Must check for interrupts and stuff here.
453 */
454 if ( uCounterReg == 0
455 || (uEFlags & X86_EFL_ZF))
456 break;
457 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
458 }
459
460 /*
461 * Done.
462 */
463 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
464 return VINF_SUCCESS;
465}
466
467
468/**
469 * Implements 'REPE SCAS'.
470 */
471IEM_CIMPL_DEF_0(RT_CONCAT4(iemCImpl_repe_scas_,OP_rAX,_m,ADDR_SIZE))
472{
473 PVM pVM = pVCpu->CTX_SUFF(pVM);
474
475 /*
476 * Setup.
477 */
478 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
479 if (uCounterReg == 0)
480 {
481 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
482 return VINF_SUCCESS;
483 }
484
485 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ES);
486 uint64_t uBaseAddr;
487 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uBaseAddr);
488 if (rcStrict != VINF_SUCCESS)
489 return rcStrict;
490
491 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
492 OP_TYPE const uValueReg = pVCpu->cpum.GstCtx.OP_rAX;
493 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
494 uint32_t uEFlags = pVCpu->cpum.GstCtx.eflags.u;
495
496 /*
497 * The loop.
498 */
499 for (;;)
500 {
501 /*
502 * Do segmentation and virtual page stuff.
503 */
504 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
505 uint32_t cLeftPage = (PAGE_SIZE - (uVirtAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
506 if (cLeftPage > uCounterReg)
507 cLeftPage = uCounterReg;
508 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
509 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
510 && ( IS_64_BIT_CODE(pVCpu)
511 || ( uAddrReg < pVCpu->cpum.GstCtx.es.u32Limit
512 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
513 )
514 )
515 {
516 RTGCPHYS GCPhysMem;
517 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_R, &GCPhysMem);
518 if (rcStrict != VINF_SUCCESS)
519 return rcStrict;
520
521 /*
522 * If we can map the page without trouble, do a block processing
523 * until the end of the current page.
524 */
525 PGMPAGEMAPLOCK PgLockMem;
526 OP_TYPE const *puMem;
527 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, (void **)&puMem, &PgLockMem);
528 if (rcStrict == VINF_SUCCESS)
529 {
530 /* Search till we find a mismatching item. */
531 OP_TYPE uTmpValue;
532 bool fQuit;
533 uint32_t i = 0;
534 do
535 {
536 uTmpValue = puMem[i++];
537 fQuit = uTmpValue != uValueReg;
538 } while (i < cLeftPage && !fQuit);
539
540 /* Update the regs. */
541 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&uValueReg, uTmpValue, &uEFlags);
542 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= i;
543 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += i * cbIncr;
544 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
545 Assert(!(uEFlags & X86_EFL_ZF) == fQuit);
546 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, puMem, &PgLockMem);
547 if ( fQuit
548 || uCounterReg == 0)
549 break;
550
551 /* If unaligned, we drop thru and do the page crossing access
552 below. Otherwise, do the next page. */
553 if (!(uVirtAddr & (OP_SIZE - 1)))
554 {
555 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
556 continue;
557 }
558 cLeftPage = 0;
559 }
560 }
561
562 /*
563 * Fallback - slow processing till the end of the current page.
564 * In the cross page boundrary case we will end up here with cLeftPage
565 * as 0, we execute one loop then.
566 */
567 do
568 {
569 OP_TYPE uTmpValue;
570 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uTmpValue, X86_SREG_ES, uAddrReg);
571 if (rcStrict != VINF_SUCCESS)
572 return rcStrict;
573 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&uValueReg, uTmpValue, &uEFlags);
574
575 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr;
576 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
577 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
578 cLeftPage--;
579 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0 || !(uEFlags & X86_EFL_ZF));
580 } while ( (int32_t)cLeftPage > 0
581 && (uEFlags & X86_EFL_ZF));
582
583 /*
584 * Next page? Must check for interrupts and stuff here.
585 */
586 if ( uCounterReg == 0
587 || !(uEFlags & X86_EFL_ZF))
588 break;
589 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
590 }
591
592 /*
593 * Done.
594 */
595 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
596 return VINF_SUCCESS;
597}
598
599
600/**
601 * Implements 'REPNE SCAS'.
602 */
603IEM_CIMPL_DEF_0(RT_CONCAT4(iemCImpl_repne_scas_,OP_rAX,_m,ADDR_SIZE))
604{
605 PVM pVM = pVCpu->CTX_SUFF(pVM);
606
607 /*
608 * Setup.
609 */
610 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
611 if (uCounterReg == 0)
612 {
613 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
614 return VINF_SUCCESS;
615 }
616
617 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ES);
618 uint64_t uBaseAddr;
619 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uBaseAddr);
620 if (rcStrict != VINF_SUCCESS)
621 return rcStrict;
622
623 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
624 OP_TYPE const uValueReg = pVCpu->cpum.GstCtx.OP_rAX;
625 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
626 uint32_t uEFlags = pVCpu->cpum.GstCtx.eflags.u;
627
628 /*
629 * The loop.
630 */
631 for (;;)
632 {
633 /*
634 * Do segmentation and virtual page stuff.
635 */
636 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
637 uint32_t cLeftPage = (PAGE_SIZE - (uVirtAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
638 if (cLeftPage > uCounterReg)
639 cLeftPage = uCounterReg;
640 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
641 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
642 && ( IS_64_BIT_CODE(pVCpu)
643 || ( uAddrReg < pVCpu->cpum.GstCtx.es.u32Limit
644 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
645 )
646 )
647 {
648 RTGCPHYS GCPhysMem;
649 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_R, &GCPhysMem);
650 if (rcStrict != VINF_SUCCESS)
651 return rcStrict;
652
653 /*
654 * If we can map the page without trouble, do a block processing
655 * until the end of the current page.
656 */
657 PGMPAGEMAPLOCK PgLockMem;
658 OP_TYPE const *puMem;
659 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, (void **)&puMem, &PgLockMem);
660 if (rcStrict == VINF_SUCCESS)
661 {
662 /* Search till we find a mismatching item. */
663 OP_TYPE uTmpValue;
664 bool fQuit;
665 uint32_t i = 0;
666 do
667 {
668 uTmpValue = puMem[i++];
669 fQuit = uTmpValue == uValueReg;
670 } while (i < cLeftPage && !fQuit);
671
672 /* Update the regs. */
673 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&uValueReg, uTmpValue, &uEFlags);
674 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= i;
675 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += i * cbIncr;
676 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
677 Assert(!!(uEFlags & X86_EFL_ZF) == fQuit);
678 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, puMem, &PgLockMem);
679 if ( fQuit
680 || uCounterReg == 0)
681 break;
682
683 /* If unaligned, we drop thru and do the page crossing access
684 below. Otherwise, do the next page. */
685 if (!(uVirtAddr & (OP_SIZE - 1)))
686 {
687 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
688 continue;
689 }
690 cLeftPage = 0;
691 }
692 }
693
694 /*
695 * Fallback - slow processing till the end of the current page.
696 * In the cross page boundrary case we will end up here with cLeftPage
697 * as 0, we execute one loop then.
698 */
699 do
700 {
701 OP_TYPE uTmpValue;
702 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uTmpValue, X86_SREG_ES, uAddrReg);
703 if (rcStrict != VINF_SUCCESS)
704 return rcStrict;
705 RT_CONCAT(iemAImpl_cmp_u,OP_SIZE)((OP_TYPE *)&uValueReg, uTmpValue, &uEFlags);
706 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr;
707 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
708 pVCpu->cpum.GstCtx.eflags.u = uEFlags;
709 cLeftPage--;
710 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0 || (uEFlags & X86_EFL_ZF));
711 } while ( (int32_t)cLeftPage > 0
712 && !(uEFlags & X86_EFL_ZF));
713
714 /*
715 * Next page? Must check for interrupts and stuff here.
716 */
717 if ( uCounterReg == 0
718 || (uEFlags & X86_EFL_ZF))
719 break;
720 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, uEFlags);
721 }
722
723 /*
724 * Done.
725 */
726 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
727 return VINF_SUCCESS;
728}
729
730
731
732
733/**
734 * Implements 'REP MOVS'.
735 */
736IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_rep_movs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg)
737{
738 PVM pVM = pVCpu->CTX_SUFF(pVM);
739
740 /*
741 * Setup.
742 */
743 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
744 if (uCounterReg == 0)
745 {
746 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
747 return VINF_SUCCESS;
748 }
749
750 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_FROM_IDX(iEffSeg) | CPUMCTX_EXTRN_ES);
751
752 PCCPUMSELREGHID pSrcHid = iemSRegGetHid(pVCpu, iEffSeg);
753 uint64_t uSrcBase;
754 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pSrcHid, iEffSeg, &uSrcBase);
755 if (rcStrict != VINF_SUCCESS)
756 return rcStrict;
757
758 uint64_t uDstBase;
759 rcStrict = iemMemSegCheckWriteAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uDstBase);
760 if (rcStrict != VINF_SUCCESS)
761 return rcStrict;
762
763 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
764 ADDR_TYPE uSrcAddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
765 ADDR_TYPE uDstAddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
766
767 /*
768 * Be careful with handle bypassing.
769 */
770 if (pVCpu->iem.s.fBypassHandlers)
771 {
772 Log(("%s: declining because we're bypassing handlers\n", __FUNCTION__));
773 return VERR_IEM_ASPECT_NOT_IMPLEMENTED;
774 }
775
776 /*
777 * The loop.
778 */
779 for (;;)
780 {
781 /*
782 * Do segmentation and virtual page stuff.
783 */
784 ADDR2_TYPE uVirtSrcAddr = uSrcAddrReg + (ADDR2_TYPE)uSrcBase;
785 ADDR2_TYPE uVirtDstAddr = uDstAddrReg + (ADDR2_TYPE)uDstBase;
786 uint32_t cLeftSrcPage = (PAGE_SIZE - (uVirtSrcAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
787 if (cLeftSrcPage > uCounterReg)
788 cLeftSrcPage = uCounterReg;
789 uint32_t cLeftDstPage = (PAGE_SIZE - (uVirtDstAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
790 uint32_t cLeftPage = RT_MIN(cLeftSrcPage, cLeftDstPage);
791
792 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
793 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
794 && ( IS_64_BIT_CODE(pVCpu)
795 || ( uSrcAddrReg < pSrcHid->u32Limit
796 && uSrcAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pSrcHid->u32Limit
797 && uDstAddrReg < pVCpu->cpum.GstCtx.es.u32Limit
798 && uDstAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
799 )
800 )
801 {
802 RTGCPHYS GCPhysSrcMem;
803 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtSrcAddr, IEM_ACCESS_DATA_R, &GCPhysSrcMem);
804 if (rcStrict != VINF_SUCCESS)
805 return rcStrict;
806
807 RTGCPHYS GCPhysDstMem;
808 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtDstAddr, IEM_ACCESS_DATA_W, &GCPhysDstMem);
809 if (rcStrict != VINF_SUCCESS)
810 return rcStrict;
811
812 /*
813 * If we can map the page without trouble, do a block processing
814 * until the end of the current page.
815 */
816 PGMPAGEMAPLOCK PgLockDstMem;
817 OP_TYPE *puDstMem;
818 rcStrict = iemMemPageMap(pVCpu, GCPhysDstMem, IEM_ACCESS_DATA_W, (void **)&puDstMem, &PgLockDstMem);
819 if (rcStrict == VINF_SUCCESS)
820 {
821 PGMPAGEMAPLOCK PgLockSrcMem;
822 OP_TYPE const *puSrcMem;
823 rcStrict = iemMemPageMap(pVCpu, GCPhysSrcMem, IEM_ACCESS_DATA_R, (void **)&puSrcMem, &PgLockSrcMem);
824 if (rcStrict == VINF_SUCCESS)
825 {
826 Assert( (GCPhysSrcMem >> PAGE_SHIFT) != (GCPhysDstMem >> PAGE_SHIFT)
827 || ((uintptr_t)puSrcMem >> PAGE_SHIFT) == ((uintptr_t)puDstMem >> PAGE_SHIFT));
828
829 /* Perform the operation exactly (don't use memcpy to avoid
830 having to consider how its implementation would affect
831 any overlapping source and destination area). */
832 OP_TYPE const *puSrcCur = puSrcMem;
833 OP_TYPE *puDstCur = puDstMem;
834 uint32_t cTodo = cLeftPage;
835 while (cTodo-- > 0)
836 *puDstCur++ = *puSrcCur++;
837
838 /* Update the registers. */
839 pVCpu->cpum.GstCtx.ADDR_rSI = uSrcAddrReg += cLeftPage * cbIncr;
840 pVCpu->cpum.GstCtx.ADDR_rDI = uDstAddrReg += cLeftPage * cbIncr;
841 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cLeftPage;
842
843 iemMemPageUnmap(pVCpu, GCPhysSrcMem, IEM_ACCESS_DATA_R, puSrcMem, &PgLockSrcMem);
844 iemMemPageUnmap(pVCpu, GCPhysDstMem, IEM_ACCESS_DATA_W, puDstMem, &PgLockDstMem);
845
846 if (uCounterReg == 0)
847 break;
848 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
849 continue;
850 }
851 iemMemPageUnmap(pVCpu, GCPhysDstMem, IEM_ACCESS_DATA_W, puDstMem, &PgLockDstMem);
852 }
853 }
854
855 /*
856 * Fallback - slow processing till the end of the current page.
857 * In the cross page boundrary case we will end up here with cLeftPage
858 * as 0, we execute one loop then.
859 */
860 do
861 {
862 OP_TYPE uValue;
863 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue, iEffSeg, uSrcAddrReg);
864 if (rcStrict != VINF_SUCCESS)
865 return rcStrict;
866 rcStrict = RT_CONCAT(iemMemStoreDataU,OP_SIZE)(pVCpu, X86_SREG_ES, uDstAddrReg, uValue);
867 if (rcStrict != VINF_SUCCESS)
868 return rcStrict;
869
870 pVCpu->cpum.GstCtx.ADDR_rSI = uSrcAddrReg += cbIncr;
871 pVCpu->cpum.GstCtx.ADDR_rDI = uDstAddrReg += cbIncr;
872 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
873 cLeftPage--;
874 IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
875 } while ((int32_t)cLeftPage > 0);
876
877 /*
878 * Next page. Must check for interrupts and stuff here.
879 */
880 if (uCounterReg == 0)
881 break;
882 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
883 }
884
885 /*
886 * Done.
887 */
888 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
889 return VINF_SUCCESS;
890}
891
892
893/**
894 * Implements 'REP STOS'.
895 */
896IEM_CIMPL_DEF_0(RT_CONCAT4(iemCImpl_stos_,OP_rAX,_m,ADDR_SIZE))
897{
898 PVM pVM = pVCpu->CTX_SUFF(pVM);
899
900 /*
901 * Setup.
902 */
903 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
904 if (uCounterReg == 0)
905 {
906 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
907 return VINF_SUCCESS;
908 }
909
910 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ES);
911
912 uint64_t uBaseAddr;
913 VBOXSTRICTRC rcStrict = iemMemSegCheckWriteAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uBaseAddr);
914 if (rcStrict != VINF_SUCCESS)
915 return rcStrict;
916
917 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
918 OP_TYPE const uValue = pVCpu->cpum.GstCtx.OP_rAX;
919 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
920
921 /*
922 * Be careful with handle bypassing.
923 */
924 /** @todo Permit doing a page if correctly aligned. */
925 if (pVCpu->iem.s.fBypassHandlers)
926 {
927 Log(("%s: declining because we're bypassing handlers\n", __FUNCTION__));
928 return VERR_IEM_ASPECT_NOT_IMPLEMENTED;
929 }
930
931 /*
932 * The loop.
933 */
934 for (;;)
935 {
936 /*
937 * Do segmentation and virtual page stuff.
938 */
939 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
940 uint32_t cLeftPage = (PAGE_SIZE - (uVirtAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
941 if (cLeftPage > uCounterReg)
942 cLeftPage = uCounterReg;
943 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
944 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
945 && ( IS_64_BIT_CODE(pVCpu)
946 || ( uAddrReg < pVCpu->cpum.GstCtx.es.u32Limit
947 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
948 )
949 )
950 {
951 RTGCPHYS GCPhysMem;
952 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_W, &GCPhysMem);
953 if (rcStrict != VINF_SUCCESS)
954 return rcStrict;
955
956 /*
957 * If we can map the page without trouble, do a block processing
958 * until the end of the current page.
959 */
960 PGMPAGEMAPLOCK PgLockMem;
961 OP_TYPE *puMem;
962 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_W, (void **)&puMem, &PgLockMem);
963 if (rcStrict == VINF_SUCCESS)
964 {
965 /* Update the regs first so we can loop on cLeftPage. */
966 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cLeftPage;
967 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cLeftPage * cbIncr;
968
969 /* Do the memsetting. */
970#if OP_SIZE == 8
971 memset(puMem, uValue, cLeftPage);
972/*#elif OP_SIZE == 32
973 ASMMemFill32(puMem, cLeftPage * (OP_SIZE / 8), uValue);*/
974#else
975 while (cLeftPage-- > 0)
976 *puMem++ = uValue;
977#endif
978
979 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_W, puMem, &PgLockMem);
980
981 if (uCounterReg == 0)
982 break;
983
984 /* If unaligned, we drop thru and do the page crossing access
985 below. Otherwise, do the next page. */
986 if (!(uVirtAddr & (OP_SIZE - 1)))
987 {
988 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
989 continue;
990 }
991 cLeftPage = 0;
992 }
993 }
994
995 /*
996 * Fallback - slow processing till the end of the current page.
997 * In the cross page boundrary case we will end up here with cLeftPage
998 * as 0, we execute one loop then.
999 */
1000 do
1001 {
1002 rcStrict = RT_CONCAT(iemMemStoreDataU,OP_SIZE)(pVCpu, X86_SREG_ES, uAddrReg, uValue);
1003 if (rcStrict != VINF_SUCCESS)
1004 return rcStrict;
1005 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr;
1006 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1007 cLeftPage--;
1008 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1009 } while ((int32_t)cLeftPage > 0);
1010
1011 /*
1012 * Next page. Must check for interrupts and stuff here.
1013 */
1014 if (uCounterReg == 0)
1015 break;
1016 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1017 }
1018
1019 /*
1020 * Done.
1021 */
1022 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1023 return VINF_SUCCESS;
1024}
1025
1026
1027/**
1028 * Implements 'REP LODS'.
1029 */
1030IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_lods_,OP_rAX,_m,ADDR_SIZE), int8_t, iEffSeg)
1031{
1032 PVM pVM = pVCpu->CTX_SUFF(pVM);
1033
1034 /*
1035 * Setup.
1036 */
1037 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
1038 if (uCounterReg == 0)
1039 {
1040 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1041 return VINF_SUCCESS;
1042 }
1043
1044 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_FROM_IDX(iEffSeg));
1045 PCCPUMSELREGHID pSrcHid = iemSRegGetHid(pVCpu, iEffSeg);
1046 uint64_t uBaseAddr;
1047 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pSrcHid, iEffSeg, &uBaseAddr);
1048 if (rcStrict != VINF_SUCCESS)
1049 return rcStrict;
1050
1051 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
1052 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
1053
1054 /*
1055 * The loop.
1056 */
1057 for (;;)
1058 {
1059 /*
1060 * Do segmentation and virtual page stuff.
1061 */
1062 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
1063 uint32_t cLeftPage = (PAGE_SIZE - (uVirtAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
1064 if (cLeftPage > uCounterReg)
1065 cLeftPage = uCounterReg;
1066 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
1067 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
1068 && ( IS_64_BIT_CODE(pVCpu)
1069 || ( uAddrReg < pSrcHid->u32Limit
1070 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pSrcHid->u32Limit)
1071 )
1072 )
1073 {
1074 RTGCPHYS GCPhysMem;
1075 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_R, &GCPhysMem);
1076 if (rcStrict != VINF_SUCCESS)
1077 return rcStrict;
1078
1079 /*
1080 * If we can map the page without trouble, we can get away with
1081 * just reading the last value on the page.
1082 */
1083 PGMPAGEMAPLOCK PgLockMem;
1084 OP_TYPE const *puMem;
1085 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, (void **)&puMem, &PgLockMem);
1086 if (rcStrict == VINF_SUCCESS)
1087 {
1088 /* Only get the last byte, the rest doesn't matter in direct access mode. */
1089#if OP_SIZE == 32
1090 pVCpu->cpum.GstCtx.rax = puMem[cLeftPage - 1];
1091#else
1092 pVCpu->cpum.GstCtx.OP_rAX = puMem[cLeftPage - 1];
1093#endif
1094 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cLeftPage;
1095 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cLeftPage * cbIncr;
1096 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, puMem, &PgLockMem);
1097
1098 if (uCounterReg == 0)
1099 break;
1100
1101 /* If unaligned, we drop thru and do the page crossing access
1102 below. Otherwise, do the next page. */
1103 if (!(uVirtAddr & (OP_SIZE - 1)))
1104 {
1105 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1106 continue;
1107 }
1108 cLeftPage = 0;
1109 }
1110 }
1111
1112 /*
1113 * Fallback - slow processing till the end of the current page.
1114 * In the cross page boundrary case we will end up here with cLeftPage
1115 * as 0, we execute one loop then.
1116 */
1117 do
1118 {
1119 OP_TYPE uTmpValue;
1120 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uTmpValue, iEffSeg, uAddrReg);
1121 if (rcStrict != VINF_SUCCESS)
1122 return rcStrict;
1123#if OP_SIZE == 32
1124 pVCpu->cpum.GstCtx.rax = uTmpValue;
1125#else
1126 pVCpu->cpum.GstCtx.OP_rAX = uTmpValue;
1127#endif
1128 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cbIncr;
1129 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1130 cLeftPage--;
1131 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1132 } while ((int32_t)cLeftPage > 0);
1133
1134 if (rcStrict != VINF_SUCCESS)
1135 break;
1136
1137 /*
1138 * Next page. Must check for interrupts and stuff here.
1139 */
1140 if (uCounterReg == 0)
1141 break;
1142 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1143 }
1144
1145 /*
1146 * Done.
1147 */
1148 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1149 return VINF_SUCCESS;
1150}
1151
1152
1153#if OP_SIZE != 64
1154
1155/**
1156 * Implements 'INS' (no rep)
1157 */
1158IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_ins_op,OP_SIZE,_addr,ADDR_SIZE), bool, fIoChecked)
1159{
1160 PVM pVM = pVCpu->CTX_SUFF(pVM);
1161 VBOXSTRICTRC rcStrict;
1162
1163 /*
1164 * Be careful with handle bypassing.
1165 */
1166 if (pVCpu->iem.s.fBypassHandlers)
1167 {
1168 Log(("%s: declining because we're bypassing handlers\n", __FUNCTION__));
1169 return VERR_IEM_ASPECT_NOT_IMPLEMENTED;
1170 }
1171
1172 /*
1173 * ASSUMES the #GP for I/O permission is taken first, then any #GP for
1174 * segmentation and finally any #PF due to virtual address translation.
1175 * ASSUMES nothing is read from the I/O port before traps are taken.
1176 */
1177 if (!fIoChecked)
1178 {
1179 rcStrict = iemHlpCheckPortIOPermission(pVCpu, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8);
1180 if (rcStrict != VINF_SUCCESS)
1181 return rcStrict;
1182 }
1183
1184 /*
1185 * Check nested-guest I/O intercepts.
1186 */
1187#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1188 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1189 {
1190 VMXEXITINSTRINFO ExitInstrInfo;
1191 ExitInstrInfo.u = 0;
1192 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1193 ExitInstrInfo.StrIo.iSegReg = X86_SREG_ES;
1194 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_INS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, false /* fRep */,
1195 ExitInstrInfo, cbInstr);
1196 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1197 return rcStrict;
1198 }
1199#endif
1200
1201#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1202 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1203 {
1204 rcStrict = iemSvmHandleIOIntercept(pVCpu, pVCpu->cpum.GstCtx.dx, SVMIOIOTYPE_IN, OP_SIZE / 8, ADDR_SIZE, X86_SREG_ES,
1205 false /* fRep */, true /* fStrIo */, cbInstr);
1206 if (rcStrict == VINF_SVM_VMEXIT)
1207 return VINF_SUCCESS;
1208 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1209 {
1210 Log(("iemCImpl_ins_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", pVCpu->cpum.GstCtx.dx,
1211 OP_SIZE / 8, VBOXSTRICTRC_VAL(rcStrict)));
1212 return rcStrict;
1213 }
1214 }
1215#endif
1216
1217 OP_TYPE *puMem;
1218 rcStrict = iemMemMap(pVCpu, (void **)&puMem, OP_SIZE / 8, X86_SREG_ES, pVCpu->cpum.GstCtx.ADDR_rDI, IEM_ACCESS_DATA_W);
1219 if (rcStrict != VINF_SUCCESS)
1220 return rcStrict;
1221
1222 uint32_t u32Value = 0;
1223 rcStrict = IOMIOPortRead(pVM, pVCpu, pVCpu->cpum.GstCtx.dx, &u32Value, OP_SIZE / 8);
1224 if (IOM_SUCCESS(rcStrict))
1225 {
1226 *puMem = (OP_TYPE)u32Value;
1227# ifdef IN_RING3
1228 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmap(pVCpu, puMem, IEM_ACCESS_DATA_W);
1229# else
1230 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmapPostponeTroubleToR3(pVCpu, puMem, IEM_ACCESS_DATA_W);
1231# endif
1232 if (RT_LIKELY(rcStrict2 == VINF_SUCCESS))
1233 {
1234 if (!pVCpu->cpum.GstCtx.eflags.Bits.u1DF)
1235 pVCpu->cpum.GstCtx.ADDR_rDI += OP_SIZE / 8;
1236 else
1237 pVCpu->cpum.GstCtx.ADDR_rDI -= OP_SIZE / 8;
1238 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1239 }
1240 else
1241 AssertLogRelMsgFailedReturn(("rcStrict2=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict2)), RT_FAILURE_NP(rcStrict2) ? rcStrict2 : VERR_IEM_IPE_1);
1242 }
1243 return rcStrict;
1244}
1245
1246
1247/**
1248 * Implements 'REP INS'.
1249 */
1250IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_rep_ins_op,OP_SIZE,_addr,ADDR_SIZE), bool, fIoChecked)
1251{
1252 PVM pVM = pVCpu->CTX_SUFF(pVM);
1253
1254 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ES | CPUMCTX_EXTRN_TR);
1255
1256 /*
1257 * Setup.
1258 */
1259 uint16_t const u16Port = pVCpu->cpum.GstCtx.dx;
1260 VBOXSTRICTRC rcStrict;
1261 if (!fIoChecked)
1262 {
1263/** @todo check if this is too early for ecx=0. */
1264 rcStrict = iemHlpCheckPortIOPermission(pVCpu, u16Port, OP_SIZE / 8);
1265 if (rcStrict != VINF_SUCCESS)
1266 return rcStrict;
1267 }
1268
1269 /*
1270 * Check nested-guest I/O intercepts.
1271 */
1272#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1273 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1274 {
1275 VMXEXITINSTRINFO ExitInstrInfo;
1276 ExitInstrInfo.u = 0;
1277 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1278 ExitInstrInfo.StrIo.iSegReg = X86_SREG_ES;
1279 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_INS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, true /* fRep */,
1280 ExitInstrInfo, cbInstr);
1281 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1282 return rcStrict;
1283 }
1284#endif
1285
1286#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1287 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1288 {
1289 rcStrict = iemSvmHandleIOIntercept(pVCpu, u16Port, SVMIOIOTYPE_IN, OP_SIZE / 8, ADDR_SIZE, X86_SREG_ES, true /* fRep */,
1290 true /* fStrIo */, cbInstr);
1291 if (rcStrict == VINF_SVM_VMEXIT)
1292 return VINF_SUCCESS;
1293 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1294 {
1295 Log(("iemCImpl_rep_ins_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", u16Port, OP_SIZE / 8,
1296 VBOXSTRICTRC_VAL(rcStrict)));
1297 return rcStrict;
1298 }
1299 }
1300#endif
1301
1302 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
1303 if (uCounterReg == 0)
1304 {
1305 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1306 return VINF_SUCCESS;
1307 }
1308
1309 uint64_t uBaseAddr;
1310 rcStrict = iemMemSegCheckWriteAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uBaseAddr);
1311 if (rcStrict != VINF_SUCCESS)
1312 return rcStrict;
1313
1314 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
1315 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
1316
1317 /*
1318 * Be careful with handle bypassing.
1319 */
1320 if (pVCpu->iem.s.fBypassHandlers)
1321 {
1322 Log(("%s: declining because we're bypassing handlers\n", __FUNCTION__));
1323 return VERR_IEM_ASPECT_NOT_IMPLEMENTED;
1324 }
1325
1326 /*
1327 * The loop.
1328 */
1329 for (;;)
1330 {
1331 /*
1332 * Do segmentation and virtual page stuff.
1333 */
1334 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
1335 uint32_t cLeftPage = (PAGE_SIZE - (uVirtAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
1336 if (cLeftPage > uCounterReg)
1337 cLeftPage = uCounterReg;
1338 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
1339 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
1340 && ( IS_64_BIT_CODE(pVCpu)
1341 || ( uAddrReg < pVCpu->cpum.GstCtx.es.u32Limit
1342 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
1343 )
1344 )
1345 {
1346 RTGCPHYS GCPhysMem;
1347 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_W, &GCPhysMem);
1348 if (rcStrict != VINF_SUCCESS)
1349 return rcStrict;
1350
1351 /*
1352 * If we can map the page without trouble, use the IOM
1353 * string I/O interface to do the work.
1354 */
1355 PGMPAGEMAPLOCK PgLockMem;
1356 OP_TYPE *puMem;
1357 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_W, (void **)&puMem, &PgLockMem);
1358 if (rcStrict == VINF_SUCCESS)
1359 {
1360 uint32_t cTransfers = cLeftPage;
1361 rcStrict = IOMIOPortReadString(pVM, pVCpu, u16Port, puMem, &cTransfers, OP_SIZE / 8);
1362
1363 uint32_t cActualTransfers = cLeftPage - cTransfers;
1364 Assert(cActualTransfers <= cLeftPage);
1365 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr * cActualTransfers;
1366 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cActualTransfers;
1367 puMem += cActualTransfers;
1368
1369 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_W, puMem, &PgLockMem);
1370
1371 if (rcStrict != VINF_SUCCESS)
1372 {
1373 if (IOM_SUCCESS(rcStrict))
1374 {
1375 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1376 if (uCounterReg == 0)
1377 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1378 }
1379 return rcStrict;
1380 }
1381
1382 /* If unaligned, we drop thru and do the page crossing access
1383 below. Otherwise, do the next page. */
1384 if (uCounterReg == 0)
1385 break;
1386 if (!(uVirtAddr & (OP_SIZE - 1)))
1387 {
1388 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1389 continue;
1390 }
1391 cLeftPage = 0;
1392 }
1393 }
1394
1395 /*
1396 * Fallback - slow processing till the end of the current page.
1397 * In the cross page boundrary case we will end up here with cLeftPage
1398 * as 0, we execute one loop then.
1399 *
1400 * Note! We ASSUME the CPU will raise #PF or #GP before access the
1401 * I/O port, otherwise it wouldn't really be restartable.
1402 */
1403 /** @todo investigate what the CPU actually does with \#PF/\#GP
1404 * during INS. */
1405 do
1406 {
1407 OP_TYPE *puMem;
1408 rcStrict = iemMemMap(pVCpu, (void **)&puMem, OP_SIZE / 8, X86_SREG_ES, uAddrReg, IEM_ACCESS_DATA_W);
1409 if (rcStrict != VINF_SUCCESS)
1410 return rcStrict;
1411
1412 uint32_t u32Value = 0;
1413 rcStrict = IOMIOPortRead(pVM, pVCpu, u16Port, &u32Value, OP_SIZE / 8);
1414 if (!IOM_SUCCESS(rcStrict))
1415 {
1416 iemMemRollback(pVCpu);
1417 return rcStrict;
1418 }
1419
1420 *puMem = (OP_TYPE)u32Value;
1421# ifdef IN_RING3
1422 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmap(pVCpu, puMem, IEM_ACCESS_DATA_W);
1423# else
1424 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmapPostponeTroubleToR3(pVCpu, puMem, IEM_ACCESS_DATA_W);
1425# endif
1426 if (rcStrict2 == VINF_SUCCESS)
1427 { /* likely */ }
1428 else
1429 AssertLogRelMsgFailedReturn(("rcStrict2=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict2)),
1430 RT_FAILURE(rcStrict2) ? rcStrict2 : VERR_IEM_IPE_1);
1431
1432 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr;
1433 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1434
1435 cLeftPage--;
1436 if (rcStrict != VINF_SUCCESS)
1437 {
1438 if (uCounterReg == 0)
1439 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1440 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1441 return rcStrict;
1442 }
1443
1444 IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1445 } while ((int32_t)cLeftPage > 0);
1446
1447
1448 /*
1449 * Next page. Must check for interrupts and stuff here.
1450 */
1451 if (uCounterReg == 0)
1452 break;
1453 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1454 }
1455
1456 /*
1457 * Done.
1458 */
1459 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1460 return VINF_SUCCESS;
1461}
1462
1463
1464/**
1465 * Implements 'OUTS' (no rep)
1466 */
1467IEM_CIMPL_DEF_2(RT_CONCAT4(iemCImpl_outs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg, bool, fIoChecked)
1468{
1469 PVM pVM = pVCpu->CTX_SUFF(pVM);
1470 VBOXSTRICTRC rcStrict;
1471
1472 /*
1473 * ASSUMES the #GP for I/O permission is taken first, then any #GP for
1474 * segmentation and finally any #PF due to virtual address translation.
1475 * ASSUMES nothing is read from the I/O port before traps are taken.
1476 */
1477 if (!fIoChecked)
1478 {
1479 rcStrict = iemHlpCheckPortIOPermission(pVCpu, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8);
1480 if (rcStrict != VINF_SUCCESS)
1481 return rcStrict;
1482 }
1483
1484 /*
1485 * Check nested-guest I/O intercepts.
1486 */
1487#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1488 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1489 {
1490 VMXEXITINSTRINFO ExitInstrInfo;
1491 ExitInstrInfo.u = 0;
1492 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1493 ExitInstrInfo.StrIo.iSegReg = iEffSeg;
1494 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_OUTS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, false /* fRep */,
1495 ExitInstrInfo, cbInstr);
1496 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1497 return rcStrict;
1498 }
1499#endif
1500
1501#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1502 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1503 {
1504 rcStrict = iemSvmHandleIOIntercept(pVCpu, pVCpu->cpum.GstCtx.dx, SVMIOIOTYPE_OUT, OP_SIZE / 8, ADDR_SIZE, iEffSeg,
1505 false /* fRep */, true /* fStrIo */, cbInstr);
1506 if (rcStrict == VINF_SVM_VMEXIT)
1507 return VINF_SUCCESS;
1508 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1509 {
1510 Log(("iemCImpl_outs_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", pVCpu->cpum.GstCtx.dx,
1511 OP_SIZE / 8, VBOXSTRICTRC_VAL(rcStrict)));
1512 return rcStrict;
1513 }
1514 }
1515#endif
1516
1517 OP_TYPE uValue;
1518 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue, iEffSeg, pVCpu->cpum.GstCtx.ADDR_rSI);
1519 if (rcStrict == VINF_SUCCESS)
1520 {
1521 rcStrict = IOMIOPortWrite(pVM, pVCpu, pVCpu->cpum.GstCtx.dx, uValue, OP_SIZE / 8);
1522 if (IOM_SUCCESS(rcStrict))
1523 {
1524 if (!pVCpu->cpum.GstCtx.eflags.Bits.u1DF)
1525 pVCpu->cpum.GstCtx.ADDR_rSI += OP_SIZE / 8;
1526 else
1527 pVCpu->cpum.GstCtx.ADDR_rSI -= OP_SIZE / 8;
1528 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1529 if (rcStrict != VINF_SUCCESS)
1530 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1531 }
1532 }
1533 return rcStrict;
1534}
1535
1536
1537/**
1538 * Implements 'REP OUTS'.
1539 */
1540IEM_CIMPL_DEF_2(RT_CONCAT4(iemCImpl_rep_outs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg, bool, fIoChecked)
1541{
1542 PVM pVM = pVCpu->CTX_SUFF(pVM);
1543
1544 /*
1545 * Setup.
1546 */
1547 uint16_t const u16Port = pVCpu->cpum.GstCtx.dx;
1548 VBOXSTRICTRC rcStrict;
1549 if (!fIoChecked)
1550 {
1551/** @todo check if this is too early for ecx=0. */
1552 rcStrict = iemHlpCheckPortIOPermission(pVCpu, u16Port, OP_SIZE / 8);
1553 if (rcStrict != VINF_SUCCESS)
1554 return rcStrict;
1555 }
1556
1557 /*
1558 * Check nested-guest I/O intercepts.
1559 */
1560#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1561 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1562 {
1563 VMXEXITINSTRINFO ExitInstrInfo;
1564 ExitInstrInfo.u = 0;
1565 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1566 ExitInstrInfo.StrIo.iSegReg = iEffSeg;
1567 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_OUTS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, true /* fRep */,
1568 ExitInstrInfo, cbInstr);
1569 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1570 return rcStrict;
1571 }
1572#endif
1573
1574#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1575 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1576 {
1577 rcStrict = iemSvmHandleIOIntercept(pVCpu, u16Port, SVMIOIOTYPE_OUT, OP_SIZE / 8, ADDR_SIZE, iEffSeg, true /* fRep */,
1578 true /* fStrIo */, cbInstr);
1579 if (rcStrict == VINF_SVM_VMEXIT)
1580 return VINF_SUCCESS;
1581 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1582 {
1583 Log(("iemCImpl_rep_outs_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", u16Port, OP_SIZE / 8,
1584 VBOXSTRICTRC_VAL(rcStrict)));
1585 return rcStrict;
1586 }
1587 }
1588#endif
1589
1590 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
1591 if (uCounterReg == 0)
1592 {
1593 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1594 return VINF_SUCCESS;
1595 }
1596
1597 PCCPUMSELREGHID pHid = iemSRegGetHid(pVCpu, iEffSeg);
1598 uint64_t uBaseAddr;
1599 rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pHid, iEffSeg, &uBaseAddr);
1600 if (rcStrict != VINF_SUCCESS)
1601 return rcStrict;
1602
1603 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
1604 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
1605
1606 /*
1607 * The loop.
1608 */
1609 for (;;)
1610 {
1611 /*
1612 * Do segmentation and virtual page stuff.
1613 */
1614 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
1615 uint32_t cLeftPage = (PAGE_SIZE - (uVirtAddr & PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
1616 if (cLeftPage > uCounterReg)
1617 cLeftPage = uCounterReg;
1618 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
1619 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
1620 && ( IS_64_BIT_CODE(pVCpu)
1621 || ( uAddrReg < pHid->u32Limit
1622 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pHid->u32Limit)
1623 )
1624 )
1625 {
1626 RTGCPHYS GCPhysMem;
1627 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_R, &GCPhysMem);
1628 if (rcStrict != VINF_SUCCESS)
1629 return rcStrict;
1630
1631 /*
1632 * If we can map the page without trouble, we use the IOM
1633 * string I/O interface to do the job.
1634 */
1635 PGMPAGEMAPLOCK PgLockMem;
1636 OP_TYPE const *puMem;
1637 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, (void **)&puMem, &PgLockMem);
1638 if (rcStrict == VINF_SUCCESS)
1639 {
1640 uint32_t cTransfers = cLeftPage;
1641 rcStrict = IOMIOPortWriteString(pVM, pVCpu, u16Port, puMem, &cTransfers, OP_SIZE / 8);
1642
1643 uint32_t cActualTransfers = cLeftPage - cTransfers;
1644 Assert(cActualTransfers <= cLeftPage);
1645 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cbIncr * cActualTransfers;
1646 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cActualTransfers;
1647 puMem += cActualTransfers;
1648
1649 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, puMem, &PgLockMem);
1650
1651 if (rcStrict != VINF_SUCCESS)
1652 {
1653 if (IOM_SUCCESS(rcStrict))
1654 {
1655 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1656 if (uCounterReg == 0)
1657 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1658 }
1659 return rcStrict;
1660 }
1661
1662 if (uCounterReg == 0)
1663 break;
1664
1665 /* If unaligned, we drop thru and do the page crossing access
1666 below. Otherwise, do the next page. */
1667 if (!(uVirtAddr & (OP_SIZE - 1)))
1668 {
1669 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1670 continue;
1671 }
1672 cLeftPage = 0;
1673 }
1674 }
1675
1676 /*
1677 * Fallback - slow processing till the end of the current page.
1678 * In the cross page boundrary case we will end up here with cLeftPage
1679 * as 0, we execute one loop then.
1680 *
1681 * Note! We ASSUME the CPU will raise #PF or #GP before access the
1682 * I/O port, otherwise it wouldn't really be restartable.
1683 */
1684 /** @todo investigate what the CPU actually does with \#PF/\#GP
1685 * during INS. */
1686 do
1687 {
1688 OP_TYPE uValue;
1689 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue, iEffSeg, uAddrReg);
1690 if (rcStrict != VINF_SUCCESS)
1691 return rcStrict;
1692
1693 rcStrict = IOMIOPortWrite(pVM, pVCpu, u16Port, uValue, OP_SIZE / 8);
1694 if (IOM_SUCCESS(rcStrict))
1695 {
1696 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cbIncr;
1697 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1698 cLeftPage--;
1699 }
1700 if (rcStrict != VINF_SUCCESS)
1701 {
1702 if (IOM_SUCCESS(rcStrict))
1703 {
1704 if (uCounterReg == 0)
1705 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1706 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1707 }
1708 return rcStrict;
1709 }
1710 IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1711 } while ((int32_t)cLeftPage > 0);
1712
1713
1714 /*
1715 * Next page. Must check for interrupts and stuff here.
1716 */
1717 if (uCounterReg == 0)
1718 break;
1719 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1720 }
1721
1722 /*
1723 * Done.
1724 */
1725 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1726 return VINF_SUCCESS;
1727}
1728
1729#endif /* OP_SIZE != 64-bit */
1730
1731
1732#undef OP_rAX
1733#undef OP_SIZE
1734#undef ADDR_SIZE
1735#undef ADDR_rDI
1736#undef ADDR_rSI
1737#undef ADDR_rCX
1738#undef ADDR_rIP
1739#undef ADDR2_TYPE
1740#undef ADDR_TYPE
1741#undef ADDR2_TYPE
1742#undef ADDR_VMXSTRIO
1743#undef IS_64_BIT_CODE
1744#undef IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN
1745#undef IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN
1746#undef IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN
1747
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette