spin_unlock(&gmap->guest_table_lock);
 }
 
+/*
+ * gmap_protect_pmd - remove access rights to memory and set pmd notification bits
+ * @pmdp: pointer to the pmd to be protected
+ * @prot: indicates access rights: PROT_NONE, PROT_READ or PROT_WRITE
+ * @bits: notification bits to set
+ *
+ * Returns:
+ * 0 if successfully protected
+ * -EAGAIN if a fixup is needed
+ * -EINVAL if unsupported notifier bits have been specified
+ *
+ * Expected to be called with sg->mm->mmap_sem in read and
+ * guest_table_lock held.
+ */
+static int gmap_protect_pmd(struct gmap *gmap, unsigned long gaddr,
+                           pmd_t *pmdp, int prot, unsigned long bits)
+{
+       int pmd_i = pmd_val(*pmdp) & _SEGMENT_ENTRY_INVALID;
+       int pmd_p = pmd_val(*pmdp) & _SEGMENT_ENTRY_PROTECT;
+
+       /* Fixup needed */
+       if ((pmd_i && (prot != PROT_NONE)) || (pmd_p && (prot == PROT_WRITE)))
+               return -EAGAIN;
+
+       if (bits & GMAP_NOTIFY_MPROT)
+               pmd_val(*pmdp) |= _SEGMENT_ENTRY_GMAP_IN;
+
+       /* Shadow GMAP protection needs split PMDs */
+       if (bits & GMAP_NOTIFY_SHADOW)
+               return -EINVAL;
+
+       return 0;
+}
+
 /*
  * gmap_protect_pte - remove access rights to memory and set pgste bits
  * @gmap: pointer to guest mapping meta data structure
 static int gmap_protect_range(struct gmap *gmap, unsigned long gaddr,
                              unsigned long len, int prot, unsigned long bits)
 {
-       unsigned long vmaddr;
+       unsigned long vmaddr, dist;
        pmd_t *pmdp;
        int rc;
 
                rc = -EAGAIN;
                pmdp = gmap_pmd_op_walk(gmap, gaddr);
                if (pmdp) {
-                       rc = gmap_protect_pte(gmap, gaddr, pmdp, prot,
-                                             bits);
-                       if (!rc) {
-                               len -= PAGE_SIZE;
-                               gaddr += PAGE_SIZE;
+                       if (!pmd_large(*pmdp)) {
+                               rc = gmap_protect_pte(gmap, gaddr, pmdp, prot,
+                                                     bits);
+                               if (!rc) {
+                                       len -= PAGE_SIZE;
+                                       gaddr += PAGE_SIZE;
+                               }
+                       } else {
+                               rc = gmap_protect_pmd(gmap, gaddr, pmdp, prot,
+                                                     bits);
+                               if (!rc) {
+                                       dist = HPAGE_SIZE - (gaddr & ~HPAGE_MASK);
+                                       len = len < dist ? 0 : len - dist;
+                                       gaddr = (gaddr & HPAGE_MASK) + HPAGE_SIZE;
+                               }
                        }
                        gmap_pmd_op_end(gmap, pmdp);
                }
                if (rc) {
+                       if (rc == -EINVAL)
+                               return rc;
+
+                       /* -EAGAIN, fixup of userspace mm and gmap */
                        vmaddr = __gmap_translate(gmap, gaddr);
                        if (IS_ERR_VALUE(vmaddr))
                                return vmaddr;