|
|
@@ -911,6 +911,40 @@ static inline void gmap_pmd_op_end(struct gmap *gmap, pmd_t *pmdp)
|
|
|
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
|
|
|
@@ -963,7 +997,7 @@ static int gmap_protect_pte(struct gmap *gmap, unsigned long gaddr,
|
|
|
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;
|
|
|
|
|
|
@@ -972,15 +1006,29 @@ static int gmap_protect_range(struct gmap *gmap, unsigned long gaddr,
|
|
|
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;
|