Commit 065fe80f authored by David Hildenbrand's avatar David Hildenbrand Committed by Cornelia Huck
Browse files

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: default avatarCornelia Huck <cohuck@redhat.com>
Reviewed-by: default avatarThomas Huth <thuth@redhat.com>
Signed-off-by: default avatarDavid Hildenbrand <david@redhat.com>
Message-Id: <20190816084708.602-7-david@redhat.com>
Signed-off-by: default avatarCornelia Huck <cohuck@redhat.com>
parent 2d3bb388
Loading
Loading
Loading
Loading
+71 −44
Original line number Diff line number Diff line
@@ -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;
}