aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2012-09-10 10:14:33 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-04-23 04:18:02 -0400
commitc5034945ce59abacdd02c5eff29f4f54df197880 (patch)
treed27a68a9ac388e13e1af097c338a162ca647f7b0 /arch/s390/mm
parent4e4d035a928340e828f633059b735901584c67a7 (diff)
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 <heiko.carstens@de.ibm.com> Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/mm')
-rw-r--r--arch/s390/mm/pgtable.c107
1 files changed, 85 insertions, 22 deletions
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;