s390x/mmu: Factor out storage key handling

Factor it out, add a comment how it all works, and also use it in the
REAL MMU.

Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190816084708.602-7-david@redhat.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
This commit is contained in:
David Hildenbrand 2019-08-16 10:47:08 +02:00 committed by Cornelia Huck
parent 2d3bb388ad
commit 065fe80fe0
1 changed files with 71 additions and 44 deletions

View File

@ -335,6 +335,75 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
return r;
}
static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
{
static S390SKeysClass *skeyclass;
static S390SKeysState *ss;
uint8_t key;
int rc;
if (unlikely(addr >= ram_size)) {
return;
}
if (unlikely(!ss)) {
ss = s390_get_skeys_device();
skeyclass = S390_SKEYS_GET_CLASS(ss);
}
/*
* Whenever we create a new TLB entry, we set the storage key reference
* bit. In case we allow write accesses, we set the storage key change
* bit. Whenever the guest changes the storage key, we have to flush the
* TLBs of all CPUs (the whole TLB or all affected entries), so that the
* next reference/change will result in an MMU fault and make us properly
* update the storage key here.
*
* Note 1: "record of references ... is not necessarily accurate",
* "change bit may be set in case no storing has occurred".
* -> We can set reference/change bits even on exceptions.
* Note 2: certain accesses seem to ignore storage keys. For example,
* DAT translation does not set reference bits for table accesses.
*
* TODO: key-controlled protection. Only CPU accesses make use of the
* PSW key. CSS accesses are different - we have to pass in the key.
*
* TODO: we have races between getting and setting the key.
*/
rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_get_skeys_nonzero(rc);
return;
}
switch (rw) {
case MMU_DATA_LOAD:
case MMU_INST_FETCH:
/*
* The TLB entry has to remain write-protected on read-faults if
* the storage key does not indicate a change already. Otherwise
* we might miss setting the change bit on write accesses.
*/
if (!(key & SK_C)) {
*flags &= ~PAGE_WRITE;
}
break;
case MMU_DATA_STORE:
key |= SK_C;
break;
default:
g_assert_not_reached();
}
/* Any store/fetch sets the reference bit */
key |= SK_R;
rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_set_skeys_nonzero(rc);
}
}
/**
* Translate a virtual (logical) address into a physical (absolute) address.
* @param vaddr the virtual address
@ -348,16 +417,9 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
target_ulong *raddr, int *flags, bool exc)
{
static S390SKeysState *ss;
static S390SKeysClass *skeyclass;
uint64_t asce;
uint8_t key;
int r;
if (unlikely(!ss)) {
ss = s390_get_skeys_device();
skeyclass = S390_SKEYS_GET_CLASS(ss);
}
*flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
if (is_low_address(vaddr & TARGET_PAGE_MASK) && lowprot_enabled(env, asc)) {
@ -414,42 +476,7 @@ nodat:
/* Convert real address -> absolute address */
*raddr = mmu_real2abs(env, *raddr);
if (*raddr < ram_size) {
r = skeyclass->get_skeys(ss, *raddr / TARGET_PAGE_SIZE, 1, &key);
if (r) {
trace_get_skeys_nonzero(r);
return 0;
}
switch (rw) {
case MMU_DATA_LOAD:
case MMU_INST_FETCH:
/*
* The TLB entry has to remain write-protected on read-faults if
* the storage key does not indicate a change already. Otherwise
* we might miss setting the change bit on write accesses.
*/
if (!(key & SK_C)) {
*flags &= ~PAGE_WRITE;
}
break;
case MMU_DATA_STORE:
key |= SK_C;
break;
default:
g_assert_not_reached();
}
/* Any store/fetch sets the reference bit */
key |= SK_R;
r = skeyclass->set_skeys(ss, *raddr / TARGET_PAGE_SIZE, 1, &key);
if (r) {
trace_set_skeys_nonzero(r);
return 0;
}
}
mmu_handle_skey(*raddr, rw, flags);
return 0;
}
@ -567,6 +594,6 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
*addr = mmu_real2abs(env, raddr & TARGET_PAGE_MASK);
/* TODO: storage key handling */
mmu_handle_skey(*addr, rw, flags);
return 0;
}