VirtualBox

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

Last change on this file since 93725 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.6 KB
Line 
1/* $Id: IEMAllCImplStrInstr.cpp.h 93554 2022-02-02 22:57:02Z vboxsync $ */
2/** @file
3 * IEM - String Instruction Implementation Code Template.
4 */
5
6/*
7 * Copyright (C) 2011-2022 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 = (GUEST_PAGE_SIZE - (uVirtSrc1Addr & GUEST_PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
174 if (cLeftSrc1Page > uCounterReg)
175 cLeftSrc1Page = uCounterReg;
176 uint32_t cLeftSrc2Page = (GUEST_PAGE_SIZE - (uVirtSrc2Addr & GUEST_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 = (GUEST_PAGE_SIZE - (uVirtSrc1Addr & GUEST_PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
344 if (cLeftSrc1Page > uCounterReg)
345 cLeftSrc1Page = uCounterReg;
346 uint32_t cLeftSrc2Page = (GUEST_PAGE_SIZE - (uVirtSrc2Addr & GUEST_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 = (GUEST_PAGE_SIZE - (uVirtAddr & GUEST_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 / 8 - 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 = (GUEST_PAGE_SIZE - (uVirtAddr & GUEST_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 / 8 - 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 = (GUEST_PAGE_SIZE - (uVirtSrcAddr & GUEST_PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
787 if (cLeftSrcPage > uCounterReg)
788 cLeftSrcPage = uCounterReg;
789 uint32_t cLeftDstPage = (GUEST_PAGE_SIZE - (uVirtDstAddr & GUEST_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 >> GUEST_PAGE_SHIFT) != (GCPhysDstMem >> GUEST_PAGE_SHIFT)
827 || ((uintptr_t)puSrcMem >> GUEST_PAGE_SHIFT) == ((uintptr_t)puDstMem >> GUEST_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 = (GUEST_PAGE_SIZE - (uVirtAddr & GUEST_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 / 8 - 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 /* If we got an invalid physical address in the page table, just skip
994 ahead to the next page or the counter reaches zero. This crazy
995 optimization is for a buggy EFI firmware that's driving me nuts. */
996 else if (rcStrict == VERR_PGM_PHYS_TLB_UNASSIGNED)
997 {
998 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cLeftPage;
999 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cLeftPage * cbIncr;
1000 if (uCounterReg == 0)
1001 break;
1002 if (!(uVirtAddr & (OP_SIZE / 8 - 1)))
1003 {
1004 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1005 continue;
1006 }
1007 }
1008 }
1009
1010 /*
1011 * Fallback - slow processing till the end of the current page.
1012 * In the cross page boundrary case we will end up here with cLeftPage
1013 * as 0, we execute one loop then.
1014 */
1015 do
1016 {
1017 rcStrict = RT_CONCAT(iemMemStoreDataU,OP_SIZE)(pVCpu, X86_SREG_ES, uAddrReg, uValue);
1018 if (rcStrict != VINF_SUCCESS)
1019 return rcStrict;
1020 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr;
1021 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1022 cLeftPage--;
1023 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1024 } while ((int32_t)cLeftPage > 0);
1025
1026 /*
1027 * Next page. Must check for interrupts and stuff here.
1028 */
1029 if (uCounterReg == 0)
1030 break;
1031 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1032 }
1033
1034 /*
1035 * Done.
1036 */
1037 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1038 return VINF_SUCCESS;
1039}
1040
1041
1042/**
1043 * Implements 'REP LODS'.
1044 */
1045IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_lods_,OP_rAX,_m,ADDR_SIZE), int8_t, iEffSeg)
1046{
1047 PVM pVM = pVCpu->CTX_SUFF(pVM);
1048
1049 /*
1050 * Setup.
1051 */
1052 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
1053 if (uCounterReg == 0)
1054 {
1055 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1056 return VINF_SUCCESS;
1057 }
1058
1059 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_SREG_FROM_IDX(iEffSeg));
1060 PCCPUMSELREGHID pSrcHid = iemSRegGetHid(pVCpu, iEffSeg);
1061 uint64_t uBaseAddr;
1062 VBOXSTRICTRC rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pSrcHid, iEffSeg, &uBaseAddr);
1063 if (rcStrict != VINF_SUCCESS)
1064 return rcStrict;
1065
1066 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
1067 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
1068
1069 /*
1070 * The loop.
1071 */
1072 for (;;)
1073 {
1074 /*
1075 * Do segmentation and virtual page stuff.
1076 */
1077 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
1078 uint32_t cLeftPage = (GUEST_PAGE_SIZE - (uVirtAddr & GUEST_PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
1079 if (cLeftPage > uCounterReg)
1080 cLeftPage = uCounterReg;
1081 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
1082 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
1083 && ( IS_64_BIT_CODE(pVCpu)
1084 || ( uAddrReg < pSrcHid->u32Limit
1085 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pSrcHid->u32Limit)
1086 )
1087 )
1088 {
1089 RTGCPHYS GCPhysMem;
1090 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_R, &GCPhysMem);
1091 if (rcStrict != VINF_SUCCESS)
1092 return rcStrict;
1093
1094 /*
1095 * If we can map the page without trouble, we can get away with
1096 * just reading the last value on the page.
1097 */
1098 PGMPAGEMAPLOCK PgLockMem;
1099 OP_TYPE const *puMem;
1100 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, (void **)&puMem, &PgLockMem);
1101 if (rcStrict == VINF_SUCCESS)
1102 {
1103 /* Only get the last byte, the rest doesn't matter in direct access mode. */
1104#if OP_SIZE == 32
1105 pVCpu->cpum.GstCtx.rax = puMem[cLeftPage - 1];
1106#else
1107 pVCpu->cpum.GstCtx.OP_rAX = puMem[cLeftPage - 1];
1108#endif
1109 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cLeftPage;
1110 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cLeftPage * cbIncr;
1111 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, puMem, &PgLockMem);
1112
1113 if (uCounterReg == 0)
1114 break;
1115
1116 /* If unaligned, we drop thru and do the page crossing access
1117 below. Otherwise, do the next page. */
1118 if (!(uVirtAddr & (OP_SIZE / 8 - 1)))
1119 {
1120 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1121 continue;
1122 }
1123 cLeftPage = 0;
1124 }
1125 }
1126
1127 /*
1128 * Fallback - slow processing till the end of the current page.
1129 * In the cross page boundrary case we will end up here with cLeftPage
1130 * as 0, we execute one loop then.
1131 */
1132 do
1133 {
1134 OP_TYPE uTmpValue;
1135 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uTmpValue, iEffSeg, uAddrReg);
1136 if (rcStrict != VINF_SUCCESS)
1137 return rcStrict;
1138#if OP_SIZE == 32
1139 pVCpu->cpum.GstCtx.rax = uTmpValue;
1140#else
1141 pVCpu->cpum.GstCtx.OP_rAX = uTmpValue;
1142#endif
1143 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cbIncr;
1144 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1145 cLeftPage--;
1146 IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1147 } while ((int32_t)cLeftPage > 0);
1148
1149 if (rcStrict != VINF_SUCCESS)
1150 break;
1151
1152 /*
1153 * Next page. Must check for interrupts and stuff here.
1154 */
1155 if (uCounterReg == 0)
1156 break;
1157 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1158 }
1159
1160 /*
1161 * Done.
1162 */
1163 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1164 return VINF_SUCCESS;
1165}
1166
1167
1168#if OP_SIZE != 64
1169
1170/**
1171 * Implements 'INS' (no rep)
1172 */
1173IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_ins_op,OP_SIZE,_addr,ADDR_SIZE), bool, fIoChecked)
1174{
1175 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1176 VBOXSTRICTRC rcStrict;
1177
1178 /*
1179 * Be careful with handle bypassing.
1180 */
1181 if (pVCpu->iem.s.fBypassHandlers)
1182 {
1183 Log(("%s: declining because we're bypassing handlers\n", __FUNCTION__));
1184 return VERR_IEM_ASPECT_NOT_IMPLEMENTED;
1185 }
1186
1187 /*
1188 * ASSUMES the #GP for I/O permission is taken first, then any #GP for
1189 * segmentation and finally any #PF due to virtual address translation.
1190 * ASSUMES nothing is read from the I/O port before traps are taken.
1191 */
1192 if (!fIoChecked)
1193 {
1194 rcStrict = iemHlpCheckPortIOPermission(pVCpu, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8);
1195 if (rcStrict != VINF_SUCCESS)
1196 return rcStrict;
1197 }
1198
1199 /*
1200 * Check nested-guest I/O intercepts.
1201 */
1202#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1203 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1204 {
1205 VMXEXITINSTRINFO ExitInstrInfo;
1206 ExitInstrInfo.u = 0;
1207 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1208 ExitInstrInfo.StrIo.iSegReg = X86_SREG_ES;
1209 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_INS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, false /* fRep */,
1210 ExitInstrInfo, cbInstr);
1211 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1212 return rcStrict;
1213 }
1214#endif
1215
1216#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1217 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1218 {
1219 rcStrict = iemSvmHandleIOIntercept(pVCpu, pVCpu->cpum.GstCtx.dx, SVMIOIOTYPE_IN, OP_SIZE / 8, ADDR_SIZE, X86_SREG_ES,
1220 false /* fRep */, true /* fStrIo */, cbInstr);
1221 if (rcStrict == VINF_SVM_VMEXIT)
1222 return VINF_SUCCESS;
1223 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1224 {
1225 Log(("iemCImpl_ins_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", pVCpu->cpum.GstCtx.dx,
1226 OP_SIZE / 8, VBOXSTRICTRC_VAL(rcStrict)));
1227 return rcStrict;
1228 }
1229 }
1230#endif
1231
1232 OP_TYPE *puMem;
1233 rcStrict = iemMemMap(pVCpu, (void **)&puMem, OP_SIZE / 8, X86_SREG_ES, pVCpu->cpum.GstCtx.ADDR_rDI, IEM_ACCESS_DATA_W);
1234 if (rcStrict != VINF_SUCCESS)
1235 return rcStrict;
1236
1237 uint32_t u32Value = 0;
1238 rcStrict = IOMIOPortRead(pVM, pVCpu, pVCpu->cpum.GstCtx.dx, &u32Value, OP_SIZE / 8);
1239 if (IOM_SUCCESS(rcStrict))
1240 {
1241 *puMem = (OP_TYPE)u32Value;
1242# ifdef IN_RING3
1243 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmap(pVCpu, puMem, IEM_ACCESS_DATA_W);
1244# else
1245 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmapPostponeTroubleToR3(pVCpu, puMem, IEM_ACCESS_DATA_W);
1246# endif
1247 if (RT_LIKELY(rcStrict2 == VINF_SUCCESS))
1248 {
1249 if (!pVCpu->cpum.GstCtx.eflags.Bits.u1DF)
1250 pVCpu->cpum.GstCtx.ADDR_rDI += OP_SIZE / 8;
1251 else
1252 pVCpu->cpum.GstCtx.ADDR_rDI -= OP_SIZE / 8;
1253 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1254 }
1255 else
1256 AssertLogRelMsgFailedReturn(("rcStrict2=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict2)), RT_FAILURE_NP(rcStrict2) ? rcStrict2 : VERR_IEM_IPE_1);
1257 }
1258 return rcStrict;
1259}
1260
1261
1262/**
1263 * Implements 'REP INS'.
1264 */
1265IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_rep_ins_op,OP_SIZE,_addr,ADDR_SIZE), bool, fIoChecked)
1266{
1267 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1268
1269 IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ES | CPUMCTX_EXTRN_TR);
1270
1271 /*
1272 * Setup.
1273 */
1274 uint16_t const u16Port = pVCpu->cpum.GstCtx.dx;
1275 VBOXSTRICTRC rcStrict;
1276 if (!fIoChecked)
1277 {
1278/** @todo check if this is too early for ecx=0. */
1279 rcStrict = iemHlpCheckPortIOPermission(pVCpu, u16Port, OP_SIZE / 8);
1280 if (rcStrict != VINF_SUCCESS)
1281 return rcStrict;
1282 }
1283
1284 /*
1285 * Check nested-guest I/O intercepts.
1286 */
1287#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1288 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1289 {
1290 VMXEXITINSTRINFO ExitInstrInfo;
1291 ExitInstrInfo.u = 0;
1292 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1293 ExitInstrInfo.StrIo.iSegReg = X86_SREG_ES;
1294 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_INS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, true /* fRep */,
1295 ExitInstrInfo, cbInstr);
1296 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1297 return rcStrict;
1298 }
1299#endif
1300
1301#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1302 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1303 {
1304 rcStrict = iemSvmHandleIOIntercept(pVCpu, u16Port, SVMIOIOTYPE_IN, OP_SIZE / 8, ADDR_SIZE, X86_SREG_ES, true /* fRep */,
1305 true /* fStrIo */, cbInstr);
1306 if (rcStrict == VINF_SVM_VMEXIT)
1307 return VINF_SUCCESS;
1308 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1309 {
1310 Log(("iemCImpl_rep_ins_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", u16Port, OP_SIZE / 8,
1311 VBOXSTRICTRC_VAL(rcStrict)));
1312 return rcStrict;
1313 }
1314 }
1315#endif
1316
1317 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
1318 if (uCounterReg == 0)
1319 {
1320 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1321 return VINF_SUCCESS;
1322 }
1323
1324 uint64_t uBaseAddr;
1325 rcStrict = iemMemSegCheckWriteAccessEx(pVCpu, iemSRegUpdateHid(pVCpu, &pVCpu->cpum.GstCtx.es), X86_SREG_ES, &uBaseAddr);
1326 if (rcStrict != VINF_SUCCESS)
1327 return rcStrict;
1328
1329 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
1330 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rDI;
1331
1332 /*
1333 * Be careful with handle bypassing.
1334 */
1335 if (pVCpu->iem.s.fBypassHandlers)
1336 {
1337 Log(("%s: declining because we're bypassing handlers\n", __FUNCTION__));
1338 return VERR_IEM_ASPECT_NOT_IMPLEMENTED;
1339 }
1340
1341 /*
1342 * The loop.
1343 */
1344 for (;;)
1345 {
1346 /*
1347 * Do segmentation and virtual page stuff.
1348 */
1349 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
1350 uint32_t cLeftPage = (GUEST_PAGE_SIZE - (uVirtAddr & GUEST_PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
1351 if (cLeftPage > uCounterReg)
1352 cLeftPage = uCounterReg;
1353 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
1354 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
1355 && ( IS_64_BIT_CODE(pVCpu)
1356 || ( uAddrReg < pVCpu->cpum.GstCtx.es.u32Limit
1357 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pVCpu->cpum.GstCtx.es.u32Limit)
1358 )
1359 )
1360 {
1361 RTGCPHYS GCPhysMem;
1362 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_W, &GCPhysMem);
1363 if (rcStrict != VINF_SUCCESS)
1364 return rcStrict;
1365
1366 /*
1367 * If we can map the page without trouble, use the IOM
1368 * string I/O interface to do the work.
1369 */
1370 PGMPAGEMAPLOCK PgLockMem;
1371 OP_TYPE *puMem;
1372 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_W, (void **)&puMem, &PgLockMem);
1373 if (rcStrict == VINF_SUCCESS)
1374 {
1375 uint32_t cTransfers = cLeftPage;
1376 rcStrict = IOMIOPortReadString(pVM, pVCpu, u16Port, puMem, &cTransfers, OP_SIZE / 8);
1377
1378 uint32_t cActualTransfers = cLeftPage - cTransfers;
1379 Assert(cActualTransfers <= cLeftPage);
1380 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr * cActualTransfers;
1381 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cActualTransfers;
1382 puMem += cActualTransfers;
1383
1384 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_W, puMem, &PgLockMem);
1385
1386 if (rcStrict != VINF_SUCCESS)
1387 {
1388 if (IOM_SUCCESS(rcStrict))
1389 {
1390 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1391 if (uCounterReg == 0)
1392 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1393 }
1394 return rcStrict;
1395 }
1396
1397 /* If unaligned, we drop thru and do the page crossing access
1398 below. Otherwise, do the next page. */
1399 if (uCounterReg == 0)
1400 break;
1401 if (!(uVirtAddr & (OP_SIZE / 8 - 1)))
1402 {
1403 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1404 continue;
1405 }
1406 cLeftPage = 0;
1407 }
1408 }
1409
1410 /*
1411 * Fallback - slow processing till the end of the current page.
1412 * In the cross page boundrary case we will end up here with cLeftPage
1413 * as 0, we execute one loop then.
1414 *
1415 * Note! We ASSUME the CPU will raise #PF or #GP before access the
1416 * I/O port, otherwise it wouldn't really be restartable.
1417 */
1418 /** @todo investigate what the CPU actually does with \#PF/\#GP
1419 * during INS. */
1420 do
1421 {
1422 OP_TYPE *puMem;
1423 rcStrict = iemMemMap(pVCpu, (void **)&puMem, OP_SIZE / 8, X86_SREG_ES, uAddrReg, IEM_ACCESS_DATA_W);
1424 if (rcStrict != VINF_SUCCESS)
1425 return rcStrict;
1426
1427 uint32_t u32Value = 0;
1428 rcStrict = IOMIOPortRead(pVM, pVCpu, u16Port, &u32Value, OP_SIZE / 8);
1429 if (!IOM_SUCCESS(rcStrict))
1430 {
1431 iemMemRollback(pVCpu);
1432 return rcStrict;
1433 }
1434
1435 *puMem = (OP_TYPE)u32Value;
1436# ifdef IN_RING3
1437 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmap(pVCpu, puMem, IEM_ACCESS_DATA_W);
1438# else
1439 VBOXSTRICTRC rcStrict2 = iemMemCommitAndUnmapPostponeTroubleToR3(pVCpu, puMem, IEM_ACCESS_DATA_W);
1440# endif
1441 if (rcStrict2 == VINF_SUCCESS)
1442 { /* likely */ }
1443 else
1444 AssertLogRelMsgFailedReturn(("rcStrict2=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict2)),
1445 RT_FAILURE(rcStrict2) ? rcStrict2 : VERR_IEM_IPE_1);
1446
1447 pVCpu->cpum.GstCtx.ADDR_rDI = uAddrReg += cbIncr;
1448 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1449
1450 cLeftPage--;
1451 if (rcStrict != VINF_SUCCESS)
1452 {
1453 if (uCounterReg == 0)
1454 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1455 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1456 return rcStrict;
1457 }
1458
1459 IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1460 } while ((int32_t)cLeftPage > 0);
1461
1462
1463 /*
1464 * Next page. Must check for interrupts and stuff here.
1465 */
1466 if (uCounterReg == 0)
1467 break;
1468 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1469 }
1470
1471 /*
1472 * Done.
1473 */
1474 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1475 return VINF_SUCCESS;
1476}
1477
1478
1479/**
1480 * Implements 'OUTS' (no rep)
1481 */
1482IEM_CIMPL_DEF_2(RT_CONCAT4(iemCImpl_outs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg, bool, fIoChecked)
1483{
1484 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1485 VBOXSTRICTRC rcStrict;
1486
1487 /*
1488 * ASSUMES the #GP for I/O permission is taken first, then any #GP for
1489 * segmentation and finally any #PF due to virtual address translation.
1490 * ASSUMES nothing is read from the I/O port before traps are taken.
1491 */
1492 if (!fIoChecked)
1493 {
1494 rcStrict = iemHlpCheckPortIOPermission(pVCpu, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8);
1495 if (rcStrict != VINF_SUCCESS)
1496 return rcStrict;
1497 }
1498
1499 /*
1500 * Check nested-guest I/O intercepts.
1501 */
1502#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1503 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1504 {
1505 VMXEXITINSTRINFO ExitInstrInfo;
1506 ExitInstrInfo.u = 0;
1507 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1508 ExitInstrInfo.StrIo.iSegReg = iEffSeg;
1509 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_OUTS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, false /* fRep */,
1510 ExitInstrInfo, cbInstr);
1511 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1512 return rcStrict;
1513 }
1514#endif
1515
1516#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1517 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1518 {
1519 rcStrict = iemSvmHandleIOIntercept(pVCpu, pVCpu->cpum.GstCtx.dx, SVMIOIOTYPE_OUT, OP_SIZE / 8, ADDR_SIZE, iEffSeg,
1520 false /* fRep */, true /* fStrIo */, cbInstr);
1521 if (rcStrict == VINF_SVM_VMEXIT)
1522 return VINF_SUCCESS;
1523 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1524 {
1525 Log(("iemCImpl_outs_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", pVCpu->cpum.GstCtx.dx,
1526 OP_SIZE / 8, VBOXSTRICTRC_VAL(rcStrict)));
1527 return rcStrict;
1528 }
1529 }
1530#endif
1531
1532 OP_TYPE uValue;
1533 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue, iEffSeg, pVCpu->cpum.GstCtx.ADDR_rSI);
1534 if (rcStrict == VINF_SUCCESS)
1535 {
1536 rcStrict = IOMIOPortWrite(pVM, pVCpu, pVCpu->cpum.GstCtx.dx, uValue, OP_SIZE / 8);
1537 if (IOM_SUCCESS(rcStrict))
1538 {
1539 if (!pVCpu->cpum.GstCtx.eflags.Bits.u1DF)
1540 pVCpu->cpum.GstCtx.ADDR_rSI += OP_SIZE / 8;
1541 else
1542 pVCpu->cpum.GstCtx.ADDR_rSI -= OP_SIZE / 8;
1543 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1544 if (rcStrict != VINF_SUCCESS)
1545 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1546 }
1547 }
1548 return rcStrict;
1549}
1550
1551
1552/**
1553 * Implements 'REP OUTS'.
1554 */
1555IEM_CIMPL_DEF_2(RT_CONCAT4(iemCImpl_rep_outs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg, bool, fIoChecked)
1556{
1557 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1558
1559 /*
1560 * Setup.
1561 */
1562 uint16_t const u16Port = pVCpu->cpum.GstCtx.dx;
1563 VBOXSTRICTRC rcStrict;
1564 if (!fIoChecked)
1565 {
1566/** @todo check if this is too early for ecx=0. */
1567 rcStrict = iemHlpCheckPortIOPermission(pVCpu, u16Port, OP_SIZE / 8);
1568 if (rcStrict != VINF_SUCCESS)
1569 return rcStrict;
1570 }
1571
1572 /*
1573 * Check nested-guest I/O intercepts.
1574 */
1575#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
1576 if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
1577 {
1578 VMXEXITINSTRINFO ExitInstrInfo;
1579 ExitInstrInfo.u = 0;
1580 ExitInstrInfo.StrIo.u3AddrSize = ADDR_VMXSTRIO;
1581 ExitInstrInfo.StrIo.iSegReg = iEffSeg;
1582 rcStrict = iemVmxVmexitInstrStrIo(pVCpu, VMXINSTRID_IO_OUTS, pVCpu->cpum.GstCtx.dx, OP_SIZE / 8, true /* fRep */,
1583 ExitInstrInfo, cbInstr);
1584 if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
1585 return rcStrict;
1586 }
1587#endif
1588
1589#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
1590 if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_IOIO_PROT))
1591 {
1592 rcStrict = iemSvmHandleIOIntercept(pVCpu, u16Port, SVMIOIOTYPE_OUT, OP_SIZE / 8, ADDR_SIZE, iEffSeg, true /* fRep */,
1593 true /* fStrIo */, cbInstr);
1594 if (rcStrict == VINF_SVM_VMEXIT)
1595 return VINF_SUCCESS;
1596 if (rcStrict != VINF_SVM_INTERCEPT_NOT_ACTIVE)
1597 {
1598 Log(("iemCImpl_rep_outs_op: iemSvmHandleIOIntercept failed (u16Port=%#x, cbReg=%u) rc=%Rrc\n", u16Port, OP_SIZE / 8,
1599 VBOXSTRICTRC_VAL(rcStrict)));
1600 return rcStrict;
1601 }
1602 }
1603#endif
1604
1605 ADDR_TYPE uCounterReg = pVCpu->cpum.GstCtx.ADDR_rCX;
1606 if (uCounterReg == 0)
1607 {
1608 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1609 return VINF_SUCCESS;
1610 }
1611
1612 PCCPUMSELREGHID pHid = iemSRegGetHid(pVCpu, iEffSeg);
1613 uint64_t uBaseAddr;
1614 rcStrict = iemMemSegCheckReadAccessEx(pVCpu, pHid, iEffSeg, &uBaseAddr);
1615 if (rcStrict != VINF_SUCCESS)
1616 return rcStrict;
1617
1618 int8_t const cbIncr = pVCpu->cpum.GstCtx.eflags.Bits.u1DF ? -(OP_SIZE / 8) : (OP_SIZE / 8);
1619 ADDR_TYPE uAddrReg = pVCpu->cpum.GstCtx.ADDR_rSI;
1620
1621 /*
1622 * The loop.
1623 */
1624 for (;;)
1625 {
1626 /*
1627 * Do segmentation and virtual page stuff.
1628 */
1629 ADDR2_TYPE uVirtAddr = uAddrReg + (ADDR2_TYPE)uBaseAddr;
1630 uint32_t cLeftPage = (GUEST_PAGE_SIZE - (uVirtAddr & GUEST_PAGE_OFFSET_MASK)) / (OP_SIZE / 8);
1631 if (cLeftPage > uCounterReg)
1632 cLeftPage = uCounterReg;
1633 if ( cLeftPage > 0 /* can be null if unaligned, do one fallback round. */
1634 && cbIncr > 0 /** @todo Implement reverse direction string ops. */
1635 && ( IS_64_BIT_CODE(pVCpu)
1636 || ( uAddrReg < pHid->u32Limit
1637 && uAddrReg + (cLeftPage * (OP_SIZE / 8)) <= pHid->u32Limit)
1638 )
1639 )
1640 {
1641 RTGCPHYS GCPhysMem;
1642 rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, uVirtAddr, IEM_ACCESS_DATA_R, &GCPhysMem);
1643 if (rcStrict != VINF_SUCCESS)
1644 return rcStrict;
1645
1646 /*
1647 * If we can map the page without trouble, we use the IOM
1648 * string I/O interface to do the job.
1649 */
1650 PGMPAGEMAPLOCK PgLockMem;
1651 OP_TYPE const *puMem;
1652 rcStrict = iemMemPageMap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, (void **)&puMem, &PgLockMem);
1653 if (rcStrict == VINF_SUCCESS)
1654 {
1655 uint32_t cTransfers = cLeftPage;
1656 rcStrict = IOMIOPortWriteString(pVM, pVCpu, u16Port, puMem, &cTransfers, OP_SIZE / 8);
1657
1658 uint32_t cActualTransfers = cLeftPage - cTransfers;
1659 Assert(cActualTransfers <= cLeftPage);
1660 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cbIncr * cActualTransfers;
1661 pVCpu->cpum.GstCtx.ADDR_rCX = uCounterReg -= cActualTransfers;
1662 puMem += cActualTransfers;
1663
1664 iemMemPageUnmap(pVCpu, GCPhysMem, IEM_ACCESS_DATA_R, puMem, &PgLockMem);
1665
1666 if (rcStrict != VINF_SUCCESS)
1667 {
1668 if (IOM_SUCCESS(rcStrict))
1669 {
1670 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1671 if (uCounterReg == 0)
1672 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1673 }
1674 return rcStrict;
1675 }
1676
1677 if (uCounterReg == 0)
1678 break;
1679
1680 /* If unaligned, we drop thru and do the page crossing access
1681 below. Otherwise, do the next page. */
1682 if (!(uVirtAddr & (OP_SIZE / 8 - 1)))
1683 {
1684 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1685 continue;
1686 }
1687 cLeftPage = 0;
1688 }
1689 }
1690
1691 /*
1692 * Fallback - slow processing till the end of the current page.
1693 * In the cross page boundrary case we will end up here with cLeftPage
1694 * as 0, we execute one loop then.
1695 *
1696 * Note! We ASSUME the CPU will raise #PF or #GP before access the
1697 * I/O port, otherwise it wouldn't really be restartable.
1698 */
1699 /** @todo investigate what the CPU actually does with \#PF/\#GP
1700 * during INS. */
1701 do
1702 {
1703 OP_TYPE uValue;
1704 rcStrict = RT_CONCAT(iemMemFetchDataU,OP_SIZE)(pVCpu, &uValue, iEffSeg, uAddrReg);
1705 if (rcStrict != VINF_SUCCESS)
1706 return rcStrict;
1707
1708 rcStrict = IOMIOPortWrite(pVM, pVCpu, u16Port, uValue, OP_SIZE / 8);
1709 if (IOM_SUCCESS(rcStrict))
1710 {
1711 pVCpu->cpum.GstCtx.ADDR_rSI = uAddrReg += cbIncr;
1712 pVCpu->cpum.GstCtx.ADDR_rCX = --uCounterReg;
1713 cLeftPage--;
1714 }
1715 if (rcStrict != VINF_SUCCESS)
1716 {
1717 if (IOM_SUCCESS(rcStrict))
1718 {
1719 if (uCounterReg == 0)
1720 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1721 rcStrict = iemSetPassUpStatus(pVCpu, rcStrict);
1722 }
1723 return rcStrict;
1724 }
1725 IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN(pVM, pVCpu, uCounterReg == 0);
1726 } while ((int32_t)cLeftPage > 0);
1727
1728
1729 /*
1730 * Next page. Must check for interrupts and stuff here.
1731 */
1732 if (uCounterReg == 0)
1733 break;
1734 IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN(pVM, pVCpu, pVCpu->cpum.GstCtx.eflags.u);
1735 }
1736
1737 /*
1738 * Done.
1739 */
1740 iemRegAddToRipAndClearRF(pVCpu, cbInstr);
1741 return VINF_SUCCESS;
1742}
1743
1744#endif /* OP_SIZE != 64-bit */
1745
1746
1747#undef OP_rAX
1748#undef OP_SIZE
1749#undef ADDR_SIZE
1750#undef ADDR_rDI
1751#undef ADDR_rSI
1752#undef ADDR_rCX
1753#undef ADDR_rIP
1754#undef ADDR2_TYPE
1755#undef ADDR_TYPE
1756#undef ADDR2_TYPE
1757#undef ADDR_VMXSTRIO
1758#undef IS_64_BIT_CODE
1759#undef IEM_CHECK_FF_YIELD_REPSTR_MAYBE_RETURN
1760#undef IEM_CHECK_FF_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN
1761#undef IEM_CHECK_FF_CPU_HIGH_PRIORITY_POST_REPSTR_MAYBE_RETURN
1762
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