VirtualBox

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

Last change on this file since 66974 was 66581, checked in by vboxsync, 8 years ago

VMM: Nested Hw.virt: Implemented various SVM intercepts in IEM, addressed some todos.

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