diff options
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 2 | ||||
-rw-r--r-- | arch/s390/mm/pgtable.c | 107 |
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); | |||
759 | int gmap_map_segment(struct gmap *gmap, unsigned long from, | 759 | int gmap_map_segment(struct gmap *gmap, unsigned long from, |
760 | unsigned long to, unsigned long length); | 760 | unsigned long to, unsigned long length); |
761 | int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); | 761 | int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); |
762 | unsigned long __gmap_translate(unsigned long address, struct gmap *); | ||
763 | unsigned long gmap_translate(unsigned long address, struct gmap *); | ||
762 | unsigned long __gmap_fault(unsigned long address, struct gmap *); | 764 | unsigned long __gmap_fault(unsigned long address, struct gmap *); |
763 | unsigned long gmap_fault(unsigned long address, struct gmap *); | 765 | unsigned long gmap_fault(unsigned long address, struct gmap *); |
764 | void gmap_discard(unsigned long from, unsigned long to, struct gmap *); | 766 | void 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 | } |
380 | EXPORT_SYMBOL_GPL(gmap_map_segment); | 380 | EXPORT_SYMBOL_GPL(gmap_map_segment); |
381 | 381 | ||
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 | |||
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 | */ |
385 | unsigned long __gmap_fault(unsigned long address, struct gmap *gmap) | 460 | unsigned 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; |