}
 EXPORT_SYMBOL_GPL(gmap_map_segment);
 
+static unsigned long *gmap_table_walk(unsigned long address, struct gmap *gmap)
+{
+       unsigned long *table;
+
+       table = gmap->table + ((address >> 53) & 0x7ff);
+       if (unlikely(*table & _REGION_ENTRY_INV))
+               return ERR_PTR(-EFAULT);
+       table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
+       table = table + ((address >> 42) & 0x7ff);
+       if (unlikely(*table & _REGION_ENTRY_INV))
+               return ERR_PTR(-EFAULT);
+       table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
+       table = table + ((address >> 31) & 0x7ff);
+       if (unlikely(*table & _REGION_ENTRY_INV))
+               return ERR_PTR(-EFAULT);
+       table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
+       table = table + ((address >> 20) & 0x7ff);
+       return table;
+}
+
+/**
+ * __gmap_translate - translate a guest address to a user space address
+ * @address: guest address
+ * @gmap: pointer to guest mapping meta data structure
+ *
+ * Returns user space address which corresponds to the guest address or
+ * -EFAULT if no such mapping exists.
+ * This function does not establish potentially missing page table entries.
+ * The mmap_sem of the mm that belongs to the address space must be held
+ * when this function gets called.
+ */
+unsigned long __gmap_translate(unsigned long address, struct gmap *gmap)
+{
+       unsigned long *segment_ptr, vmaddr, segment;
+       struct gmap_pgtable *mp;
+       struct page *page;
+
+       current->thread.gmap_addr = address;
+       segment_ptr = gmap_table_walk(address, gmap);
+       if (IS_ERR(segment_ptr))
+               return PTR_ERR(segment_ptr);
+       /* Convert the gmap address to an mm address. */
+       segment = *segment_ptr;
+       if (!(segment & _SEGMENT_ENTRY_INV)) {
+               page = pfn_to_page(segment >> PAGE_SHIFT);
+               mp = (struct gmap_pgtable *) page->index;
+               return mp->vmaddr | (address & ~PMD_MASK);
+       } else if (segment & _SEGMENT_ENTRY_RO) {
+               vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
+               return vmaddr | (address & ~PMD_MASK);
+       }
+       return -EFAULT;
+}
+EXPORT_SYMBOL_GPL(__gmap_translate);
+
+/**
+ * gmap_translate - translate a guest address to a user space address
+ * @address: guest address
+ * @gmap: pointer to guest mapping meta data structure
+ *
+ * Returns user space address which corresponds to the guest address or
+ * -EFAULT if no such mapping exists.
+ * This function does not establish potentially missing page table entries.
+ */
+unsigned long gmap_translate(unsigned long address, struct gmap *gmap)
+{
+       unsigned long rc;
+
+       down_read(&gmap->mm->mmap_sem);
+       rc = __gmap_translate(address, gmap);
+       up_read(&gmap->mm->mmap_sem);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(gmap_translate);
+
 /*
  * this function is assumed to be called with mmap_sem held
  */
 unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
 {
-       unsigned long *table, vmaddr, segment;
-       struct mm_struct *mm;
+       unsigned long *segment_ptr, vmaddr, segment;
+       struct vm_area_struct *vma;
        struct gmap_pgtable *mp;
        struct gmap_rmap *rmap;
-       struct vm_area_struct *vma;
+       struct mm_struct *mm;
        struct page *page;
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;
 
        current->thread.gmap_addr = address;
-       mm = gmap->mm;
-       /* Walk the gmap address space page table */
-       table = gmap->table + ((address >> 53) & 0x7ff);
-       if (unlikely(*table & _REGION_ENTRY_INV))
-               return -EFAULT;
-       table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
-       table = table + ((address >> 42) & 0x7ff);
-       if (unlikely(*table & _REGION_ENTRY_INV))
+       segment_ptr = gmap_table_walk(address, gmap);
+       if (IS_ERR(segment_ptr))
                return -EFAULT;
-       table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
-       table = table + ((address >> 31) & 0x7ff);
-       if (unlikely(*table & _REGION_ENTRY_INV))
-               return -EFAULT;
-       table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
-       table = table + ((address >> 20) & 0x7ff);
-
        /* Convert the gmap address to an mm address. */
-       segment = *table;
-       if (likely(!(segment & _SEGMENT_ENTRY_INV))) {
+       segment = *segment_ptr;
+       if (!(segment & _SEGMENT_ENTRY_INV)) {
                page = pfn_to_page(segment >> PAGE_SHIFT);
                mp = (struct gmap_pgtable *) page->index;
                return mp->vmaddr | (address & ~PMD_MASK);
        } else if (segment & _SEGMENT_ENTRY_RO) {
+               mm = gmap->mm;
                vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
                vma = find_vma(mm, vmaddr);
                if (!vma || vma->vm_start > vmaddr)
                /* Link gmap segment table entry location to page table. */
                page = pmd_page(*pmd);
                mp = (struct gmap_pgtable *) page->index;
-               rmap->entry = table;
+               rmap->entry = segment_ptr;
                spin_lock(&mm->page_table_lock);
                list_add(&rmap->list, &mp->mapper);
                spin_unlock(&mm->page_table_lock);
                /* Set gmap segment table entry to page table. */
-               *table = pmd_val(*pmd) & PAGE_MASK;
+               *segment_ptr = pmd_val(*pmd) & PAGE_MASK;
                return vmaddr | (address & ~PMD_MASK);
        }
        return -EFAULT;