Skip to content

Commit c503494

Browse files
heicarstMartin Schwidefsky
authored andcommitted
s390/mm,gmap: implement gmap_translate()
Implement gmap_translate() function which translates a guest absolute address to a user space process address without establishing the guest page table entries. This is useful for kvm guest address translations where no memory access is expected to happen soon (e.g. tprot exception handler). Signed-off-by: Heiko Carstens <[email protected]> Reviewed-by: Christian Borntraeger <[email protected]> Signed-off-by: Martin Schwidefsky <[email protected]>
1 parent 4e4d035 commit c503494

File tree

2 files changed

+87
-22
lines changed

2 files changed

+87
-22
lines changed

arch/s390/include/asm/pgtable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,8 @@ void gmap_disable(struct gmap *gmap);
760760
int gmap_map_segment(struct gmap *gmap, unsigned long from,
761761
unsigned long to, unsigned long length);
762762
int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len);
763+
unsigned long __gmap_translate(unsigned long address, struct gmap *);
764+
unsigned long gmap_translate(unsigned long address, struct gmap *);
763765
unsigned long __gmap_fault(unsigned long address, struct gmap *);
764766
unsigned long gmap_fault(unsigned long address, struct gmap *);
765767
void gmap_discard(unsigned long from, unsigned long to, struct gmap *);

arch/s390/mm/pgtable.c

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -379,45 +379,108 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from,
379379
}
380380
EXPORT_SYMBOL_GPL(gmap_map_segment);
381381

382+
static unsigned long *gmap_table_walk(unsigned long address, struct gmap *gmap)
383+
{
384+
unsigned long *table;
385+
386+
table = gmap->table + ((address >> 53) & 0x7ff);
387+
if (unlikely(*table & _REGION_ENTRY_INV))
388+
return ERR_PTR(-EFAULT);
389+
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
390+
table = table + ((address >> 42) & 0x7ff);
391+
if (unlikely(*table & _REGION_ENTRY_INV))
392+
return ERR_PTR(-EFAULT);
393+
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
394+
table = table + ((address >> 31) & 0x7ff);
395+
if (unlikely(*table & _REGION_ENTRY_INV))
396+
return ERR_PTR(-EFAULT);
397+
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
398+
table = table + ((address >> 20) & 0x7ff);
399+
return table;
400+
}
401+
402+
/**
403+
* __gmap_translate - translate a guest address to a user space address
404+
* @address: guest address
405+
* @gmap: pointer to guest mapping meta data structure
406+
*
407+
* Returns user space address which corresponds to the guest address or
408+
* -EFAULT if no such mapping exists.
409+
* This function does not establish potentially missing page table entries.
410+
* The mmap_sem of the mm that belongs to the address space must be held
411+
* when this function gets called.
412+
*/
413+
unsigned long __gmap_translate(unsigned long address, struct gmap *gmap)
414+
{
415+
unsigned long *segment_ptr, vmaddr, segment;
416+
struct gmap_pgtable *mp;
417+
struct page *page;
418+
419+
current->thread.gmap_addr = address;
420+
segment_ptr = gmap_table_walk(address, gmap);
421+
if (IS_ERR(segment_ptr))
422+
return PTR_ERR(segment_ptr);
423+
/* Convert the gmap address to an mm address. */
424+
segment = *segment_ptr;
425+
if (!(segment & _SEGMENT_ENTRY_INV)) {
426+
page = pfn_to_page(segment >> PAGE_SHIFT);
427+
mp = (struct gmap_pgtable *) page->index;
428+
return mp->vmaddr | (address & ~PMD_MASK);
429+
} else if (segment & _SEGMENT_ENTRY_RO) {
430+
vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
431+
return vmaddr | (address & ~PMD_MASK);
432+
}
433+
return -EFAULT;
434+
}
435+
EXPORT_SYMBOL_GPL(__gmap_translate);
436+
437+
/**
438+
* gmap_translate - translate a guest address to a user space address
439+
* @address: guest address
440+
* @gmap: pointer to guest mapping meta data structure
441+
*
442+
* Returns user space address which corresponds to the guest address or
443+
* -EFAULT if no such mapping exists.
444+
* This function does not establish potentially missing page table entries.
445+
*/
446+
unsigned long gmap_translate(unsigned long address, struct gmap *gmap)
447+
{
448+
unsigned long rc;
449+
450+
down_read(&gmap->mm->mmap_sem);
451+
rc = __gmap_translate(address, gmap);
452+
up_read(&gmap->mm->mmap_sem);
453+
return rc;
454+
}
455+
EXPORT_SYMBOL_GPL(gmap_translate);
456+
382457
/*
383458
* this function is assumed to be called with mmap_sem held
384459
*/
385460
unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
386461
{
387-
unsigned long *table, vmaddr, segment;
388-
struct mm_struct *mm;
462+
unsigned long *segment_ptr, vmaddr, segment;
463+
struct vm_area_struct *vma;
389464
struct gmap_pgtable *mp;
390465
struct gmap_rmap *rmap;
391-
struct vm_area_struct *vma;
466+
struct mm_struct *mm;
392467
struct page *page;
393468
pgd_t *pgd;
394469
pud_t *pud;
395470
pmd_t *pmd;
396471

397472
current->thread.gmap_addr = address;
398-
mm = gmap->mm;
399-
/* Walk the gmap address space page table */
400-
table = gmap->table + ((address >> 53) & 0x7ff);
401-
if (unlikely(*table & _REGION_ENTRY_INV))
402-
return -EFAULT;
403-
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
404-
table = table + ((address >> 42) & 0x7ff);
405-
if (unlikely(*table & _REGION_ENTRY_INV))
473+
segment_ptr = gmap_table_walk(address, gmap);
474+
if (IS_ERR(segment_ptr))
406475
return -EFAULT;
407-
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
408-
table = table + ((address >> 31) & 0x7ff);
409-
if (unlikely(*table & _REGION_ENTRY_INV))
410-
return -EFAULT;
411-
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
412-
table = table + ((address >> 20) & 0x7ff);
413-
414476
/* Convert the gmap address to an mm address. */
415-
segment = *table;
416-
if (likely(!(segment & _SEGMENT_ENTRY_INV))) {
477+
segment = *segment_ptr;
478+
if (!(segment & _SEGMENT_ENTRY_INV)) {
417479
page = pfn_to_page(segment >> PAGE_SHIFT);
418480
mp = (struct gmap_pgtable *) page->index;
419481
return mp->vmaddr | (address & ~PMD_MASK);
420482
} else if (segment & _SEGMENT_ENTRY_RO) {
483+
mm = gmap->mm;
421484
vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
422485
vma = find_vma(mm, vmaddr);
423486
if (!vma || vma->vm_start > vmaddr)
@@ -441,12 +504,12 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
441504
/* Link gmap segment table entry location to page table. */
442505
page = pmd_page(*pmd);
443506
mp = (struct gmap_pgtable *) page->index;
444-
rmap->entry = table;
507+
rmap->entry = segment_ptr;
445508
spin_lock(&mm->page_table_lock);
446509
list_add(&rmap->list, &mp->mapper);
447510
spin_unlock(&mm->page_table_lock);
448511
/* Set gmap segment table entry to page table. */
449-
*table = pmd_val(*pmd) & PAGE_MASK;
512+
*segment_ptr = pmd_val(*pmd) & PAGE_MASK;
450513
return vmaddr | (address & ~PMD_MASK);
451514
}
452515
return -EFAULT;

0 commit comments

Comments
 (0)