aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/pgtable.h2
-rw-r--r--arch/s390/mm/pgtable.c107
2 files changed, 87 insertions, 22 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index 4a2930844d43..75b8750a16ff 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -759,6 +759,8 @@ void gmap_disable(struct gmap *gmap);
759int gmap_map_segment(struct gmap *gmap, unsigned long from, 759int gmap_map_segment(struct gmap *gmap, unsigned long from,
760 unsigned long to, unsigned long length); 760 unsigned long to, unsigned long length);
761int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); 761int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len);
762unsigned long __gmap_translate(unsigned long address, struct gmap *);
763unsigned long gmap_translate(unsigned long address, struct gmap *);
762unsigned long __gmap_fault(unsigned long address, struct gmap *); 764unsigned long __gmap_fault(unsigned long address, struct gmap *);
763unsigned long gmap_fault(unsigned long address, struct gmap *); 765unsigned long gmap_fault(unsigned long address, struct gmap *);
764void gmap_discard(unsigned long from, unsigned long to, struct gmap *); 766void gmap_discard(unsigned long from, unsigned long to, struct gmap *);
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index ae44d2a34313..2accf7113d13 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -379,45 +379,108 @@ out_unmap:
379} 379}
380EXPORT_SYMBOL_GPL(gmap_map_segment); 380EXPORT_SYMBOL_GPL(gmap_map_segment);
381 381
382static 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 */
413unsigned 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}
435EXPORT_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 */
446unsigned 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}
455EXPORT_SYMBOL_GPL(gmap_translate);
456
382/* 457/*
383 * this function is assumed to be called with mmap_sem held 458 * this function is assumed to be called with mmap_sem held
384 */ 459 */
385unsigned long __gmap_fault(unsigned long address, struct gmap *gmap) 460unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
386{ 461{
387 unsigned long *table, vmaddr, segment; 462 unsigned long *segment_ptr, vmaddr, segment;
388 struct mm_struct *mm; 463 struct vm_area_struct *vma;
389 struct gmap_pgtable *mp; 464 struct gmap_pgtable *mp;
390 struct gmap_rmap *rmap; 465 struct gmap_rmap *rmap;
391 struct vm_area_struct *vma; 466 struct mm_struct *mm;
392 struct page *page; 467 struct page *page;
393 pgd_t *pgd; 468 pgd_t *pgd;
394 pud_t *pud; 469 pud_t *pud;
395 pmd_t *pmd; 470 pmd_t *pmd;
396 471
397 current->thread.gmap_addr = address; 472 current->thread.gmap_addr = address;
398 mm = gmap->mm; 473 segment_ptr = gmap_table_walk(address, gmap);
399 /* Walk the gmap address space page table */ 474 if (IS_ERR(segment_ptr))
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))
406 return -EFAULT; 475 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
414 /* Convert the gmap address to an mm address. */ 476 /* Convert the gmap address to an mm address. */
415 segment = *table; 477 segment = *segment_ptr;
416 if (likely(!(segment & _SEGMENT_ENTRY_INV))) { 478 if (!(segment & _SEGMENT_ENTRY_INV)) {
417 page = pfn_to_page(segment >> PAGE_SHIFT); 479 page = pfn_to_page(segment >> PAGE_SHIFT);
418 mp = (struct gmap_pgtable *) page->index; 480 mp = (struct gmap_pgtable *) page->index;
419 return mp->vmaddr | (address & ~PMD_MASK); 481 return mp->vmaddr | (address & ~PMD_MASK);
420 } else if (segment & _SEGMENT_ENTRY_RO) { 482 } else if (segment & _SEGMENT_ENTRY_RO) {
483 mm = gmap->mm;
421 vmaddr = segment & _SEGMENT_ENTRY_ORIGIN; 484 vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
422 vma = find_vma(mm, vmaddr); 485 vma = find_vma(mm, vmaddr);
423 if (!vma || vma->vm_start > vmaddr) 486 if (!vma || vma->vm_start > vmaddr)
@@ -441,12 +504,12 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
441 /* Link gmap segment table entry location to page table. */ 504 /* Link gmap segment table entry location to page table. */
442 page = pmd_page(*pmd); 505 page = pmd_page(*pmd);
443 mp = (struct gmap_pgtable *) page->index; 506 mp = (struct gmap_pgtable *) page->index;
444 rmap->entry = table; 507 rmap->entry = segment_ptr;
445 spin_lock(&mm->page_table_lock); 508 spin_lock(&mm->page_table_lock);
446 list_add(&rmap->list, &mp->mapper); 509 list_add(&rmap->list, &mp->mapper);
447 spin_unlock(&mm->page_table_lock); 510 spin_unlock(&mm->page_table_lock);
448 /* Set gmap segment table entry to page table. */ 511 /* Set gmap segment table entry to page table. */
449 *table = pmd_val(*pmd) & PAGE_MASK; 512 *segment_ptr = pmd_val(*pmd) & PAGE_MASK;
450 return vmaddr | (address & ~PMD_MASK); 513 return vmaddr | (address & ~PMD_MASK);
451 } 514 }
452 return -EFAULT; 515 return -EFAULT;