Commit 18b89809 authored by David Hildenbrand's avatar David Hildenbrand Committed by Christian Borntraeger

s390/mm: support EDAT2 for gmap shadows

If the guest is enabled for EDAT2, we can easily create shadows for
guest2 -> guest3 provided tables that make use of EDAT2.

If guest2 references a 2GB page, this memory looks consecutive for guest2,
but it does not have to be so for us. Therefore we have to create fake
segment and page tables.

This works just like EDAT1 support, so page tables are removed when the
parent table (r3t table entry) is changed.

We don't hve to care about:
- ACCF-Validity Control in RTTE
- Access-Control Bits in RTTE
- Fetch-Protection Bit in RTTE
- Common-Region Bit in RTTE

Just like for EDAT1, all bits might be dropped and there is no guaranteed
that they are active.
Acked-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarDavid Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
parent fd8d4e3a
...@@ -111,7 +111,8 @@ struct gmap *gmap_shadow(struct gmap *parent, unsigned long asce, ...@@ -111,7 +111,8 @@ struct gmap *gmap_shadow(struct gmap *parent, unsigned long asce,
int edat_level); int edat_level);
int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t); int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t);
int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t); int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t);
int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt); int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt,
int fake);
int gmap_shadow_pgt(struct gmap *sg, unsigned long saddr, unsigned long pgt, int gmap_shadow_pgt(struct gmap *sg, unsigned long saddr, unsigned long pgt,
int fake); int fake);
int gmap_shadow_pgt_lookup(struct gmap *sg, unsigned long saddr, int gmap_shadow_pgt_lookup(struct gmap *sg, unsigned long saddr,
......
...@@ -1042,17 +1042,35 @@ static int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr, ...@@ -1042,17 +1042,35 @@ static int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
return PGM_REGION_THIRD_TRANS; return PGM_REGION_THIRD_TRANS;
if (rtte.tt != TABLE_TYPE_REGION3) if (rtte.tt != TABLE_TYPE_REGION3)
return PGM_TRANSLATION_SPEC; return PGM_TRANSLATION_SPEC;
if (rtte.cr && asce.p && sg->edat_level >= 2)
return PGM_TRANSLATION_SPEC;
if (rtte.fc && sg->edat_level >= 2) {
bool prot = rtte.fc1.p;
*fake = 1;
ptr = rtte.fc1.rfaa << 31UL;
rtte.val = ptr;
rtte.fc0.p = prot;
goto shadow_sgt;
}
if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl) if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl)
return PGM_SEGMENT_TRANSLATION; return PGM_SEGMENT_TRANSLATION;
rc = gmap_shadow_sgt(sg, saddr, rtte.val); ptr = rtte.fc0.sto << 12UL;
shadow_sgt:
rc = gmap_shadow_sgt(sg, saddr, rtte.val, *fake);
if (rc) if (rc)
return rc; return rc;
ptr = rtte.fc0.sto * 4096;
/* fallthrough */ /* fallthrough */
} }
case ASCE_TYPE_SEGMENT: { case ASCE_TYPE_SEGMENT: {
union segment_table_entry ste; union segment_table_entry ste;
if (*fake) {
/* offset in 2G guest memory block */
ptr = ptr + ((unsigned long) vaddr.sx << 20UL);
ste.val = ptr;
goto shadow_pgt;
}
rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val); rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val);
if (rc) if (rc)
return rc; return rc;
......
...@@ -1631,6 +1631,7 @@ EXPORT_SYMBOL_GPL(gmap_shadow_r3t); ...@@ -1631,6 +1631,7 @@ EXPORT_SYMBOL_GPL(gmap_shadow_r3t);
* @sg: pointer to the shadow guest address space structure * @sg: pointer to the shadow guest address space structure
* @saddr: faulting address in the shadow gmap * @saddr: faulting address in the shadow gmap
* @sgt: parent gmap address of the segment table to get shadowed * @sgt: parent gmap address of the segment table to get shadowed
* @fake: sgt references contiguous guest memory block, not a sgt
* *
* Returns: 0 if successfully shadowed or already shadowed, -EAGAIN if the * Returns: 0 if successfully shadowed or already shadowed, -EAGAIN if the
* shadow table structure is incomplete, -ENOMEM if out of memory and * shadow table structure is incomplete, -ENOMEM if out of memory and
...@@ -1638,19 +1639,22 @@ EXPORT_SYMBOL_GPL(gmap_shadow_r3t); ...@@ -1638,19 +1639,22 @@ EXPORT_SYMBOL_GPL(gmap_shadow_r3t);
* *
* Called with sg->mm->mmap_sem in read. * Called with sg->mm->mmap_sem in read.
*/ */
int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt) int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt,
int fake)
{ {
unsigned long raddr, origin, offset, len; unsigned long raddr, origin, offset, len;
unsigned long *s_sgt, *table; unsigned long *s_sgt, *table;
struct page *page; struct page *page;
int rc; int rc;
BUG_ON(!gmap_is_shadow(sg)); BUG_ON(!gmap_is_shadow(sg) || (sgt & _REGION3_ENTRY_LARGE));
/* Allocate a shadow segment table */ /* Allocate a shadow segment table */
page = alloc_pages(GFP_KERNEL, 2); page = alloc_pages(GFP_KERNEL, 2);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
page->index = sgt & _REGION_ENTRY_ORIGIN; page->index = sgt & _REGION_ENTRY_ORIGIN;
if (fake)
page->index |= GMAP_SHADOW_FAKE_TABLE;
s_sgt = (unsigned long *) page_to_phys(page); s_sgt = (unsigned long *) page_to_phys(page);
/* Install shadow region second table */ /* Install shadow region second table */
spin_lock(&sg->guest_table_lock); spin_lock(&sg->guest_table_lock);
...@@ -1673,6 +1677,12 @@ int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt) ...@@ -1673,6 +1677,12 @@ int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt)
if (sg->edat_level >= 1) if (sg->edat_level >= 1)
*table |= sgt & _REGION_ENTRY_PROTECT; *table |= sgt & _REGION_ENTRY_PROTECT;
list_add(&page->lru, &sg->crst_list); list_add(&page->lru, &sg->crst_list);
if (fake) {
/* nothing to protect for fake tables */
*table &= ~_REGION_ENTRY_INVALID;
spin_unlock(&sg->guest_table_lock);
return 0;
}
spin_unlock(&sg->guest_table_lock); spin_unlock(&sg->guest_table_lock);
/* Make sgt read-only in parent gmap page table */ /* Make sgt read-only in parent gmap page table */
raddr = (saddr & 0xffffffff80000000UL) | _SHADOW_RMAP_REGION3; raddr = (saddr & 0xffffffff80000000UL) | _SHADOW_RMAP_REGION3;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment