diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h
index 560ce7b..ac84908 100644
--- a/tools/libxc/include/xenctrl.h
+++ b/tools/libxc/include/xenctrl.h
@@ -2125,6 +2125,10 @@ int xc_set_mem_access(xc_interface *xch, domid_t domain_id,
                       xenmem_access_t access, uint64_t first_pfn,
                       uint32_t nr);
 
+int xc_set_mem_access_sparse(xc_interface *xch, domid_t domain_id,
+                             xenmem_access_t access, xen_pfn_t *pages,
+                             uint32_t nr);
+
 /*
  * Gets the mem access for the given page (returned in access on success)
  */
diff --git a/tools/libxc/xc_mem_access.c b/tools/libxc/xc_mem_access.c
index eee088c..73b1caa 100644
--- a/tools/libxc/xc_mem_access.c
+++ b/tools/libxc/xc_mem_access.c
@@ -41,6 +41,38 @@ int xc_set_mem_access(xc_interface *xch,
     return do_memory_op(xch, XENMEM_access_op, &mao, sizeof(mao));
 }
 
+int xc_set_mem_access_sparse(xc_interface *xch,
+                             domid_t domain_id,
+                             xenmem_access_t access,
+                             xen_pfn_t *pages,
+                             uint32_t nr)
+{
+    DECLARE_HYPERCALL_BOUNCE(pages, nr * sizeof(xen_pfn_t), XC_HYPERCALL_BUFFER_BOUNCE_IN);
+    int rc;
+
+    xen_mem_access_op_t mao =
+    {
+        .op     = XENMEM_access_op_set_access_sparse,
+        .domid  = domain_id,
+        .access = access,
+        .nr     = nr
+    };
+
+    if ( xc_hypercall_bounce_pre(xch, pages) )
+    {
+        PERROR("Could not bounce memory for XENMEM_access_op_set_access_sparse");
+        return -1;
+    }
+
+    set_xen_guest_handle(mao.pfn_list, pages);
+
+    rc = do_memory_op(xch, XENMEM_access_op, &mao, sizeof(mao));
+
+    xc_hypercall_bounce_post(xch, pages);
+
+    return rc;
+}
+
 int xc_get_mem_access(xc_interface *xch,
                       domid_t domain_id,
                       uint64_t pfn,
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 0180f26..03461e5 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -5323,7 +5323,7 @@ static int do_altp2m_op(
         if ( a.u.set_mem_access.pad )
             rc = -EINVAL;
         else
-            rc = p2m_set_mem_access(d, _gfn(a.u.set_mem_access.gfn), 1, 0, 0,
+            rc = p2m_set_mem_access(d, _gfn(a.u.set_mem_access.gfn), NULL, 1, 0, 0,
                                     a.u.set_mem_access.hvmmem_access,
                                     a.u.set_mem_access.view);
         break;
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 812dbf6..2c45cc6 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1815,7 +1815,7 @@ int p2m_set_altp2m_mem_access(struct domain *d, struct p2m_domain *hp2m,
  * Set access type for a region of gfns.
  * If gfn == INVALID_GFN, sets the default access type.
  */
-long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
+long p2m_set_mem_access(struct domain *d, gfn_t gfn, xen_pfn_t *arr, uint32_t nr,
                         uint32_t start, uint32_t mask, xenmem_access_t access,
                         unsigned int altp2m_idx)
 {
@@ -1874,28 +1874,53 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
     if ( ap2m )
         p2m_lock(ap2m);
 
-    for ( gfn_l = gfn_x(gfn) + start; nr > start; ++gfn_l )
+    if ( !arr )
     {
-        if ( ap2m )
+        for ( gfn_l = gfn_x(gfn) + start; nr > start; ++gfn_l )
         {
-            rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
-            /* If the corresponding mfn is invalid we will just skip it */
-            if ( rc && rc != -ESRCH )
-                break;
-        }
-        else
-        {
-            mfn = p2m->get_entry(p2m, gfn_l, &t, &_a, 0, NULL, NULL);
-            rc = p2m->set_entry(p2m, gfn_l, mfn, PAGE_ORDER_4K, t, a, -1);
-            if ( rc )
+            if ( ap2m )
+            {
+                rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
+                /* If the corresponding mfn is invalid we will just skip it */
+                if ( rc && rc != -ESRCH )
+                    break;
+            }
+            else
+            {
+                mfn = p2m->get_entry(p2m, gfn_l, &t, &_a, 0, NULL, NULL);
+                rc = p2m->set_entry(p2m, gfn_l, mfn, PAGE_ORDER_4K, t, a, -1);
+                if ( rc )
+                    break;
+            }
+
+            /* Check for continuation if it's not the last iteration. */
+            if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
+            {
+                rc = start;
                 break;
+            }
         }
+    }
+    else
+    {
+        uint32_t i;
 
-        /* Check for continuation if it's not the last iteration. */
-        if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
+        for ( i = 0; i < nr; ++i )
         {
-            rc = start;
-            break;
+            if ( ap2m )
+            {
+                rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, _gfn(arr[i]));
+                /* If the corresponding mfn is invalid we will just skip it */
+                if ( rc && rc != -ESRCH )
+                    break;
+            }
+            else
+            {
+                mfn = p2m->get_entry(p2m, arr[i], &t, &_a, 0, NULL, NULL);
+                rc = p2m->set_entry(p2m, arr[i], mfn, PAGE_ORDER_4K, t, a, -1);
+                if ( rc )
+                    break;
+            }
         }
     }
 
diff --git a/xen/common/compat/memory.c b/xen/common/compat/memory.c
index 20c7671..664b8fe 100644
--- a/xen/common/compat/memory.c
+++ b/xen/common/compat/memory.c
@@ -15,7 +15,6 @@ CHECK_TYPE(domid);
 #undef compat_domid_t
 #undef xen_domid_t
 
-CHECK_mem_access_op;
 CHECK_vmemrange;
 
 #ifdef CONFIG_HAS_PASSTHROUGH
diff --git a/xen/common/mem_access.c b/xen/common/mem_access.c
index b4033f0..1768020 100644
--- a/xen/common/mem_access.c
+++ b/xen/common/mem_access.c
@@ -66,7 +66,7 @@ int mem_access_memop(unsigned long cmd,
               ((mao.pfn + mao.nr - 1) > domain_get_maximum_gpfn(d))) )
             break;
 
-        rc = p2m_set_mem_access(d, _gfn(mao.pfn), mao.nr, start_iter,
+        rc = p2m_set_mem_access(d, _gfn(mao.pfn), NULL, mao.nr, start_iter,
                                 MEMOP_CMD_MASK, mao.access, 0);
         if ( rc > 0 )
         {
@@ -76,6 +76,17 @@ int mem_access_memop(unsigned long cmd,
         }
         break;
 
+    case XENMEM_access_op_set_access_sparse:
+    {
+        xen_pfn_t *arr = xmalloc_bytes(sizeof(xen_pfn_t) * mao.nr);
+
+        // copy_from_guest(arr, mao.pfn_list, mao.nr);
+        rc = p2m_set_mem_access(d, _gfn(mao.pfn), arr, mao.nr, start_iter,
+                                MEMOP_CMD_MASK, mao.access, 0);
+        xfree(arr);
+        break;
+    }
+
     case XENMEM_access_op_get_access:
     {
         xenmem_access_t access;
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index 3badfb9..2e224e3 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -410,6 +410,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_mem_paging_op_t);
  * #define XENMEM_access_op_enable_emulate     2
  * #define XENMEM_access_op_disable_emulate    3
  */
+#define XENMEM_access_op_set_access_sparse  4
 
 typedef enum {
     XENMEM_access_n,
@@ -452,6 +453,11 @@ struct xen_mem_access_op {
      * ~0ull is used to set and get the default access for pages
      */
     uint64_aligned_t pfn;
+    /*
+     * List of pfns to set access for
+     * Used only with XENMEM_access_op_set_access_sparse
+     */
+    XEN_GUEST_HANDLE(xen_pfn_t) pfn_list;
 };
 typedef struct xen_mem_access_op xen_mem_access_op_t;
 DEFINE_XEN_GUEST_HANDLE(xen_mem_access_op_t);
diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
index b4f9077..c6723b6 100644
--- a/xen/include/xen/p2m-common.h
+++ b/xen/include/xen/p2m-common.h
@@ -49,9 +49,9 @@ int unmap_mmio_regions(struct domain *d,
  * Set access type for a region of gfns.
  * If gfn == INVALID_GFN, sets the default access type.
  */
-long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
-                        uint32_t start, uint32_t mask, xenmem_access_t access,
-                        unsigned int altp2m_idx);
+long p2m_set_mem_access(struct domain *d, gfn_t gfn, xen_pfn_t *arr,
+                        uint32_t nr, uint32_t start, uint32_t mask,
+                        xenmem_access_t access, unsigned int altp2m_idx);
 
 /*
  * Get access type for a gfn.
