diff mbox

tests/xen-access: Added vm_event emulation tests

Message ID 1491817485-16896-1-git-send-email-rcojocaru@bitdefender.com (mailing list archive)
State New, archived
Headers show

Commit Message

Razvan Cojocaru April 10, 2017, 9:44 a.m. UTC
This patch adds support for testing instruction emulation when
required by the vm_event reply sent for MEM_ACCESS events. To this
end, it adds the "emulate_write" and "emulate_exec" parameters
that behave like the old "write" and "exec" parameters, except
instead of allowing writes / executes for a hit page, they emulate
the trigger instruction. The new parameters don't mark all of the
guest's pages, instead they stop at the arbitrary low limit of
the first 1000 pages - otherwise the guest would slow to a crawl.
Since the emulator is still incomplete and has trouble with
emulating competing writes in SMP scenarios, the new tests are
only meant for debugging issues.

Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
---
 tools/tests/xen-access/xen-access.c | 38 ++++++++++++++++++++++++++++---------
 1 file changed, 29 insertions(+), 9 deletions(-)

Comments

Wei Liu April 12, 2017, 4:50 p.m. UTC | #1
On Mon, Apr 10, 2017 at 12:44:45PM +0300, Razvan Cojocaru wrote:
> This patch adds support for testing instruction emulation when
> required by the vm_event reply sent for MEM_ACCESS events. To this
> end, it adds the "emulate_write" and "emulate_exec" parameters
> that behave like the old "write" and "exec" parameters, except
> instead of allowing writes / executes for a hit page, they emulate
> the trigger instruction. The new parameters don't mark all of the
> guest's pages, instead they stop at the arbitrary low limit of
> the first 1000 pages - otherwise the guest would slow to a crawl.
> Since the emulator is still incomplete and has trouble with
> emulating competing writes in SMP scenarios, the new tests are
> only meant for debugging issues.
> 
> Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com>

FAOD this should be acked by Tamas.

I also suggest you repost this once the development window opens,
regardless whether it is acked or not.

Wei.
Razvan Cojocaru April 12, 2017, 4:53 p.m. UTC | #2
On 04/12/2017 07:50 PM, Wei Liu wrote:
> On Mon, Apr 10, 2017 at 12:44:45PM +0300, Razvan Cojocaru wrote:
>> This patch adds support for testing instruction emulation when
>> required by the vm_event reply sent for MEM_ACCESS events. To this
>> end, it adds the "emulate_write" and "emulate_exec" parameters
>> that behave like the old "write" and "exec" parameters, except
>> instead of allowing writes / executes for a hit page, they emulate
>> the trigger instruction. The new parameters don't mark all of the
>> guest's pages, instead they stop at the arbitrary low limit of
>> the first 1000 pages - otherwise the guest would slow to a crawl.
>> Since the emulator is still incomplete and has trouble with
>> emulating competing writes in SMP scenarios, the new tests are
>> only meant for debugging issues.
>>
>> Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
> 
> FAOD this should be acked by Tamas.
> 
> I also suggest you repost this once the development window opens,
> regardless whether it is acked or not.

Of course, he's probably just not near a computer or has not seen it.


Thanks,
Razvan
Tamas K Lengyel April 12, 2017, 5:11 p.m. UTC | #3
On Mon, Apr 10, 2017 at 3:44 AM, Razvan Cojocaru <rcojocaru@bitdefender.com>
wrote:

> This patch adds support for testing instruction emulation when
> required by the vm_event reply sent for MEM_ACCESS events. To this
> end, it adds the "emulate_write" and "emulate_exec" parameters
> that behave like the old "write" and "exec" parameters, except
> instead of allowing writes / executes for a hit page, they emulate
> the trigger instruction. The new parameters don't mark all of the
> guest's pages, instead they stop at the arbitrary low limit of
> the first 1000 pages - otherwise the guest would slow to a crawl.
> Since the emulator is still incomplete and has trouble with
> emulating competing writes in SMP scenarios, the new tests are
> only meant for debugging issues.
>
> Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
> ---
>  tools/tests/xen-access/xen-access.c | 38 ++++++++++++++++++++++++++++--
> -------
>  1 file changed, 29 insertions(+), 9 deletions(-)
>
> diff --git a/tools/tests/xen-access/xen-access.c
> b/tools/tests/xen-access/xen-access.c
> index ff4d289..0ba2e45 100644
> --- a/tools/tests/xen-access/xen-access.c
> +++ b/tools/tests/xen-access/xen-access.c
> @@ -335,7 +335,7 @@ static void put_response(vm_event_t *vm_event,
> vm_event_response_t *rsp)
>
>  void usage(char* progname)
>  {
> -    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
> +    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec|emulate_write|emulate_exec",
> progname);
>

These options are only for x86, so they need to be moved into the #if block
below.


>  #if defined(__i386__) || defined(__x86_64__)
>              fprintf(stderr, "|breakpoint|altp2m_write|
> altp2m_exec|debug|cpuid|desc_access");
>  #elif defined(__arm__) || defined(__aarch64__)
> @@ -369,6 +369,7 @@ int main(int argc, char *argv[])
>      int debug = 0;
>      int cpuid = 0;
>      int desc_access = 0;
> +    int emulate = 0;
>      uint16_t altp2m_view_id = 0;
>
>      char* progname = argv[0];
> @@ -404,12 +405,26 @@ int main(int argc, char *argv[])
>          after_first_access = XENMEM_access_rwx;
>          memaccess = 1;
>      }
> +    else if ( !strcmp(argv[0], "emulate_write") )
> +    {
> +        default_access = XENMEM_access_rx;
> +        after_first_access = XENMEM_access_rwx;
>

Setting after_first_access not needed.

+        emulate = 1;
> +        memaccess = 1;
> +    }
>      else if ( !strcmp(argv[0], "exec") )
>      {
>          default_access = XENMEM_access_rw;
>          after_first_access = XENMEM_access_rwx;
>          memaccess = 1;
>      }
> +    else if ( !strcmp(argv[0], "emulate_exec") )
> +    {
> +        default_access = XENMEM_access_rw;
> +        after_first_access = XENMEM_access_rwx;
>

Setting after_first_access not needed.

>
> +        emulate = 1;
> +        memaccess = 1;
> +    }
>  #if defined(__i386__) || defined(__x86_64__)
>      else if ( !strcmp(argv[0], "breakpoint") )
>      {
> @@ -536,7 +551,7 @@ int main(int argc, char *argv[])
>          }
>
>          rc = xc_set_mem_access(xch, domain_id, default_access, START_PFN,
> -                               (xenaccess->max_gpfn - START_PFN) );
> +                               emulate ? 1000 : (xenaccess->max_gpfn -
> START_PFN));
>

Why only 1000? What if the domain has less then 1000?


>
>          if ( rc < 0 )
>          {
> @@ -702,15 +717,20 @@ int main(int argc, char *argv[])
>                  }
>                  else if ( default_access != after_first_access )
>                  {
> -                    rc = xc_set_mem_access(xch, domain_id,
> after_first_access,
> -                                           req.u.mem_access.gfn, 1);
> -                    if (rc < 0)
> +                    if ( !emulate )
>                      {
> -                        ERROR("Error %d setting gfn to access_type %d\n",
> rc,
> -                              after_first_access);
> -                        interrupted = -1;
> -                        continue;
> +                        rc = xc_set_mem_access(xch, domain_id,
> after_first_access,
> +                                               req.u.mem_access.gfn, 1);
> +                        if (rc < 0)
> +                        {
> +                            ERROR("Error %d setting gfn to access_type
> %d\n", rc,
> +                                  after_first_access);
> +                            interrupted = -1;
> +                            continue;
> +                        }
>                      }
> +                    else
> +                        rsp.flags |= VM_EVENT_FLAG_EMULATE;
>                  }
>
>                  rsp.u.mem_access = req.u.mem_access;
> --
> 1.9.1
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xen.org
> https://lists.xen.org/xen-devel
>
Razvan Cojocaru April 13, 2017, 10:20 a.m. UTC | #4
On 04/12/2017 08:11 PM, Tamas K Lengyel wrote:
> 
> 
> On Mon, Apr 10, 2017 at 3:44 AM, Razvan Cojocaru
> <rcojocaru@bitdefender.com <mailto:rcojocaru@bitdefender.com>> wrote:
> 
>     This patch adds support for testing instruction emulation when
>     required by the vm_event reply sent for MEM_ACCESS events. To this
>     end, it adds the "emulate_write" and "emulate_exec" parameters
>     that behave like the old "write" and "exec" parameters, except
>     instead of allowing writes / executes for a hit page, they emulate
>     the trigger instruction. The new parameters don't mark all of the
>     guest's pages, instead they stop at the arbitrary low limit of
>     the first 1000 pages - otherwise the guest would slow to a crawl.
>     Since the emulator is still incomplete and has trouble with
>     emulating competing writes in SMP scenarios, the new tests are
>     only meant for debugging issues.
> 
>     Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com
>     <mailto:rcojocaru@bitdefender.com>>
>     ---
>      tools/tests/xen-access/xen-access.c | 38
>     ++++++++++++++++++++++++++++---------
>      1 file changed, 29 insertions(+), 9 deletions(-)
> 
>     diff --git a/tools/tests/xen-access/xen-access.c
>     b/tools/tests/xen-access/xen-access.c
>     index ff4d289..0ba2e45 100644
>     --- a/tools/tests/xen-access/xen-access.c
>     +++ b/tools/tests/xen-access/xen-access.c
>     @@ -335,7 +335,7 @@ static void put_response(vm_event_t *vm_event,
>     vm_event_response_t *rsp)
> 
>      void usage(char* progname)
>      {
>     -    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
>     +    fprintf(stderr, "Usage: %s [-m] <domain_id>
>     write|exec|emulate_write|emulate_exec", progname);
> 
> 
> These options are only for x86, so they need to be moved into the #if
> block below.

Sure.

>      #if defined(__i386__) || defined(__x86_64__)
>                  fprintf(stderr,
>     "|breakpoint|altp2m_write|altp2m_exec|debug|cpuid|desc_access");
>      #elif defined(__arm__) || defined(__aarch64__)
>     @@ -369,6 +369,7 @@ int main(int argc, char *argv[])
>          int debug = 0;
>          int cpuid = 0;
>          int desc_access = 0;
>     +    int emulate = 0;
>          uint16_t altp2m_view_id = 0;
> 
>          char* progname = argv[0];
>     @@ -404,12 +405,26 @@ int main(int argc, char *argv[])
>              after_first_access = XENMEM_access_rwx;
>              memaccess = 1;
>          }
>     +    else if ( !strcmp(argv[0], "emulate_write") )
>     +    {
>     +        default_access = XENMEM_access_rx;
>     +        after_first_access = XENMEM_access_rwx;
> 
> 
> Setting after_first_access not needed.

True. I got carried away with the copy-paste.

>     +        emulate = 1;
>     +        memaccess = 1;
>     +    }
>          else if ( !strcmp(argv[0], "exec") )
>          {
>              default_access = XENMEM_access_rw;
>              after_first_access = XENMEM_access_rwx;
>              memaccess = 1;
>          }
>     +    else if ( !strcmp(argv[0], "emulate_exec") )
>     +    {
>     +        default_access = XENMEM_access_rw;
>     +        after_first_access = XENMEM_access_rwx;
> 
> 
> Setting after_first_access not needed.

Also true.

>     +        emulate = 1;
>     +        memaccess = 1;
>     +    }
>      #if defined(__i386__) || defined(__x86_64__)
>          else if ( !strcmp(argv[0], "breakpoint") )
>          {
>     @@ -536,7 +551,7 @@ int main(int argc, char *argv[])
>              }
> 
>              rc = xc_set_mem_access(xch, domain_id, default_access,
>     START_PFN,
>     -                               (xenaccess->max_gpfn - START_PFN) );
>     +                               emulate ? 1000 :
>     (xenaccess->max_gpfn - START_PFN));
> 
> 
> Why only 1000? What if the domain has less then 1000?

Because it will kill the guest to emulate everything, and the emulator
still can't handle all instructions (this is easy to see by using all
the guest's pages and looking at the output of xl dmesg with loglvl=all
guest_loglvl=all on the Xen command line).

As for less than 1000, I'm not sure what domain that would be :), but
I'm happy to change the code to min(1000, max_gpfn).


Thanks,
Razvan
Tamas K Lengyel April 14, 2017, 6:08 p.m. UTC | #5
On Thu, Apr 13, 2017 at 4:20 AM, Razvan Cojocaru <rcojocaru@bitdefender.com>
wrote:

> On 04/12/2017 08:11 PM, Tamas K Lengyel wrote:
> >
> >
> > On Mon, Apr 10, 2017 at 3:44 AM, Razvan Cojocaru
> > <rcojocaru@bitdefender.com <mailto:rcojocaru@bitdefender.com>> wrote:
> >
> >     This patch adds support for testing instruction emulation when
> >     required by the vm_event reply sent for MEM_ACCESS events. To this
> >     end, it adds the "emulate_write" and "emulate_exec" parameters
> >     that behave like the old "write" and "exec" parameters, except
> >     instead of allowing writes / executes for a hit page, they emulate
> >     the trigger instruction. The new parameters don't mark all of the
> >     guest's pages, instead they stop at the arbitrary low limit of
> >     the first 1000 pages - otherwise the guest would slow to a crawl.
> >     Since the emulator is still incomplete and has trouble with
> >     emulating competing writes in SMP scenarios, the new tests are
> >     only meant for debugging issues.
> >
> >     Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com
> >     <mailto:rcojocaru@bitdefender.com>>
> >     ---
> >      tools/tests/xen-access/xen-access.c | 38
> >     ++++++++++++++++++++++++++++---------
> >      1 file changed, 29 insertions(+), 9 deletions(-)
> >
> >     diff --git a/tools/tests/xen-access/xen-access.c
> >     b/tools/tests/xen-access/xen-access.c
> >     index ff4d289..0ba2e45 100644
> >     --- a/tools/tests/xen-access/xen-access.c
> >     +++ b/tools/tests/xen-access/xen-access.c
> >     @@ -335,7 +335,7 @@ static void put_response(vm_event_t *vm_event,
> >     vm_event_response_t *rsp)
> >
> >      void usage(char* progname)
> >      {
> >     -    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec",
> progname);
> >     +    fprintf(stderr, "Usage: %s [-m] <domain_id>
> >     write|exec|emulate_write|emulate_exec", progname);
> >
> >
> > These options are only for x86, so they need to be moved into the #if
> > block below.
>
> Sure.
>
> >      #if defined(__i386__) || defined(__x86_64__)
> >                  fprintf(stderr,
> >     "|breakpoint|altp2m_write|altp2m_exec|debug|cpuid|desc_access");
> >      #elif defined(__arm__) || defined(__aarch64__)
> >     @@ -369,6 +369,7 @@ int main(int argc, char *argv[])
> >          int debug = 0;
> >          int cpuid = 0;
> >          int desc_access = 0;
> >     +    int emulate = 0;
> >          uint16_t altp2m_view_id = 0;
> >
> >          char* progname = argv[0];
> >     @@ -404,12 +405,26 @@ int main(int argc, char *argv[])
> >              after_first_access = XENMEM_access_rwx;
> >              memaccess = 1;
> >          }
> >     +    else if ( !strcmp(argv[0], "emulate_write") )
> >     +    {
> >     +        default_access = XENMEM_access_rx;
> >     +        after_first_access = XENMEM_access_rwx;
> >
> >
> > Setting after_first_access not needed.
>
> True. I got carried away with the copy-paste.
>
> >     +        emulate = 1;
> >     +        memaccess = 1;
> >     +    }
> >          else if ( !strcmp(argv[0], "exec") )
> >          {
> >              default_access = XENMEM_access_rw;
> >              after_first_access = XENMEM_access_rwx;
> >              memaccess = 1;
> >          }
> >     +    else if ( !strcmp(argv[0], "emulate_exec") )
> >     +    {
> >     +        default_access = XENMEM_access_rw;
> >     +        after_first_access = XENMEM_access_rwx;
> >
> >
> > Setting after_first_access not needed.
>
> Also true.
>
> >     +        emulate = 1;
> >     +        memaccess = 1;
> >     +    }
> >      #if defined(__i386__) || defined(__x86_64__)
> >          else if ( !strcmp(argv[0], "breakpoint") )
> >          {
> >     @@ -536,7 +551,7 @@ int main(int argc, char *argv[])
> >              }
> >
> >              rc = xc_set_mem_access(xch, domain_id, default_access,
> >     START_PFN,
> >     -                               (xenaccess->max_gpfn - START_PFN) );
> >     +                               emulate ? 1000 :
> >     (xenaccess->max_gpfn - START_PFN));
> >
> >
> > Why only 1000? What if the domain has less then 1000?
>
> Because it will kill the guest to emulate everything, and the emulator
> still can't handle all instructions (this is easy to see by using all
> the guest's pages and looking at the output of xl dmesg with loglvl=all
> guest_loglvl=all on the Xen command line).
>

So what's the guarantee that the emulator will work if you only do it only
up to the first 1000 pages? Seems totally arbitrary to me. If the emulator
can't handle all instructions then you would have to check that the
instruction for which you are returning the emulate flag is in the list of
instruction that can be handled.. Can such a list be derived right now?

Tamas
Razvan Cojocaru April 14, 2017, 7:03 p.m. UTC | #6
On 04/14/2017 09:08 PM, Tamas K Lengyel wrote:
> 
> 
> On Thu, Apr 13, 2017 at 4:20 AM, Razvan Cojocaru
> <rcojocaru@bitdefender.com <mailto:rcojocaru@bitdefender.com>> wrote:
> 
>     On 04/12/2017 08:11 PM, Tamas K Lengyel wrote:
>     >
>     >
>     > On Mon, Apr 10, 2017 at 3:44 AM, Razvan Cojocaru
>     >     +        emulate = 1;
>     >     +        memaccess = 1;
>     >     +    }
>     >      #if defined(__i386__) || defined(__x86_64__)
>     >          else if ( !strcmp(argv[0], "breakpoint") )
>     >          {
>     >     @@ -536,7 +551,7 @@ int main(int argc, char *argv[])
>     >              }
>     >
>     >              rc = xc_set_mem_access(xch, domain_id, default_access,
>     >     START_PFN,
>     >     -                               (xenaccess->max_gpfn - START_PFN) );
>     >     +                               emulate ? 1000 :
>     >     (xenaccess->max_gpfn - START_PFN));
>     >
>     >
>     > Why only 1000? What if the domain has less then 1000?
> 
>     Because it will kill the guest to emulate everything, and the emulator
>     still can't handle all instructions (this is easy to see by using all
>     the guest's pages and looking at the output of xl dmesg with loglvl=all
>     guest_loglvl=all on the Xen command line).
> 
> 
> So what's the guarantee that the emulator will work if you only do it
> only up to the first 1000 pages? Seems totally arbitrary to me. If the
> emulator can't handle all instructions then you would have to check that
> the instruction for which you are returning the emulate flag is in the
> list of instruction that can be handled.. Can such a list be derived
> right now?

If an instruction can't be emulated it will be shown as such in the ring
buffer used by xl dmesg. Speaking of that, I'd like to, at some point,
send a patch that sends a vm_event saying that emulation failed to
userspace when that is the case, to give it a chance to do something
else (for example use altp2m, or lift the page restrictions).

We can also probably go through the emulator code and build an exact
list of all the officially supported instructions, but I believe that
that would have to be manual work - I am not aware of a tool to extract
them or a header file that lists them in some structure. I'd love to be
wrong about this.

As for your question, there's no guarantee that the emulator will
work,obut that's not why I chose 1000. I chose that number because the
application will get less EPT events, and the guest will not be bogged
down by handling them. But in my experiments it's also less likely to
hit unhandleable instructions in the first 1000 pages since those are
usually used by the guest kernel, drivers, and so on, and are less
likely to cause problems.

In any case, I don't mind dropping the 1000 pages limit - I can always
build a custom xen-access when I need it.


Thanks,
Razvan
Tamas K Lengyel April 14, 2017, 7:29 p.m. UTC | #7
On Fri, Apr 14, 2017 at 1:03 PM, Razvan Cojocaru <rcojocaru@bitdefender.com>
wrote:

> On 04/14/2017 09:08 PM, Tamas K Lengyel wrote:
> >
> >
> > On Thu, Apr 13, 2017 at 4:20 AM, Razvan Cojocaru
> > <rcojocaru@bitdefender.com <mailto:rcojocaru@bitdefender.com>> wrote:
> >
> >     On 04/12/2017 08:11 PM, Tamas K Lengyel wrote:
> >     >
> >     >
> >     > On Mon, Apr 10, 2017 at 3:44 AM, Razvan Cojocaru
> >     >     +        emulate = 1;
> >     >     +        memaccess = 1;
> >     >     +    }
> >     >      #if defined(__i386__) || defined(__x86_64__)
> >     >          else if ( !strcmp(argv[0], "breakpoint") )
> >     >          {
> >     >     @@ -536,7 +551,7 @@ int main(int argc, char *argv[])
> >     >              }
> >     >
> >     >              rc = xc_set_mem_access(xch, domain_id, default_access,
> >     >     START_PFN,
> >     >     -                               (xenaccess->max_gpfn -
> START_PFN) );
> >     >     +                               emulate ? 1000 :
> >     >     (xenaccess->max_gpfn - START_PFN));
> >     >
> >     >
> >     > Why only 1000? What if the domain has less then 1000?
> >
> >     Because it will kill the guest to emulate everything, and the
> emulator
> >     still can't handle all instructions (this is easy to see by using all
> >     the guest's pages and looking at the output of xl dmesg with
> loglvl=all
> >     guest_loglvl=all on the Xen command line).
> >
> >
> > So what's the guarantee that the emulator will work if you only do it
> > only up to the first 1000 pages? Seems totally arbitrary to me. If the
> > emulator can't handle all instructions then you would have to check that
> > the instruction for which you are returning the emulate flag is in the
> > list of instruction that can be handled.. Can such a list be derived
> > right now?
>
> If an instruction can't be emulated it will be shown as such in the ring
> buffer used by xl dmesg. Speaking of that, I'd like to, at some point,
> send a patch that sends a vm_event saying that emulation failed to
> userspace when that is the case, to give it a chance to do something
> else (for example use altp2m, or lift the page restrictions).
>

I think that would be a much needed addition to make this system more
robust.


>
> We can also probably go through the emulator code and build an exact
> list of all the officially supported instructions, but I believe that
> that would have to be manual work - I am not aware of a tool to extract
> them or a header file that lists them in some structure. I'd love to be
> wrong about this.
>
> As for your question, there's no guarantee that the emulator will
> work,obut that's not why I chose 1000. I chose that number because the
> application will get less EPT events, and the guest will not be bogged
> down by handling them. But in my experiments it's also less likely to
> hit unhandleable instructions in the first 1000 pages since those are
> usually used by the guest kernel, drivers, and so on, and are less
> likely to cause problems.
>
> In any case, I don't mind dropping the 1000 pages limit - I can always
> build a custom xen-access when I need it.
>

I don't mind setting it only for a 1000 in the test program, just wanted to
understand rationale behind it. I think a comment in the program explaining
what has been discussed here would also be helpful.

Tamas
diff mbox

Patch

diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c
index ff4d289..0ba2e45 100644
--- a/tools/tests/xen-access/xen-access.c
+++ b/tools/tests/xen-access/xen-access.c
@@ -335,7 +335,7 @@  static void put_response(vm_event_t *vm_event, vm_event_response_t *rsp)
 
 void usage(char* progname)
 {
-    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
+    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec|emulate_write|emulate_exec", progname);
 #if defined(__i386__) || defined(__x86_64__)
             fprintf(stderr, "|breakpoint|altp2m_write|altp2m_exec|debug|cpuid|desc_access");
 #elif defined(__arm__) || defined(__aarch64__)
@@ -369,6 +369,7 @@  int main(int argc, char *argv[])
     int debug = 0;
     int cpuid = 0;
     int desc_access = 0;
+    int emulate = 0;
     uint16_t altp2m_view_id = 0;
 
     char* progname = argv[0];
@@ -404,12 +405,26 @@  int main(int argc, char *argv[])
         after_first_access = XENMEM_access_rwx;
         memaccess = 1;
     }
+    else if ( !strcmp(argv[0], "emulate_write") )
+    {
+        default_access = XENMEM_access_rx;
+        after_first_access = XENMEM_access_rwx;
+        emulate = 1;
+        memaccess = 1;
+    }
     else if ( !strcmp(argv[0], "exec") )
     {
         default_access = XENMEM_access_rw;
         after_first_access = XENMEM_access_rwx;
         memaccess = 1;
     }
+    else if ( !strcmp(argv[0], "emulate_exec") )
+    {
+        default_access = XENMEM_access_rw;
+        after_first_access = XENMEM_access_rwx;
+        emulate = 1;
+        memaccess = 1;
+    }
 #if defined(__i386__) || defined(__x86_64__)
     else if ( !strcmp(argv[0], "breakpoint") )
     {
@@ -536,7 +551,7 @@  int main(int argc, char *argv[])
         }
 
         rc = xc_set_mem_access(xch, domain_id, default_access, START_PFN,
-                               (xenaccess->max_gpfn - START_PFN) );
+                               emulate ? 1000 : (xenaccess->max_gpfn - START_PFN));
 
         if ( rc < 0 )
         {
@@ -702,15 +717,20 @@  int main(int argc, char *argv[])
                 }
                 else if ( default_access != after_first_access )
                 {
-                    rc = xc_set_mem_access(xch, domain_id, after_first_access,
-                                           req.u.mem_access.gfn, 1);
-                    if (rc < 0)
+                    if ( !emulate )
                     {
-                        ERROR("Error %d setting gfn to access_type %d\n", rc,
-                              after_first_access);
-                        interrupted = -1;
-                        continue;
+                        rc = xc_set_mem_access(xch, domain_id, after_first_access,
+                                               req.u.mem_access.gfn, 1);
+                        if (rc < 0)
+                        {
+                            ERROR("Error %d setting gfn to access_type %d\n", rc,
+                                  after_first_access);
+                            interrupted = -1;
+                            continue;
+                        }
                     }
+                    else
+                        rsp.flags |= VM_EVENT_FLAG_EMULATE;
                 }
 
                 rsp.u.mem_access = req.u.mem_access;