/* SPDX-License-Identifier: GPL-2.0-only */ /* * Storage key tests * * Copyright (c) 2018 IBM Corp * * Authors: * Janosch Frank */ #include #include #include #include #include #include #include #include #include static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2))); static void test_set_mb(void) { union skey skey, ret1, ret2; void *addr = (void *)0x10000 - 2 * PAGE_SIZE; void *end = (void *)0x10000; /* Multi block support came with EDAT 1 */ if (!test_facility(8)) return; skey.val = 0x30; while (addr < end) addr = set_storage_key_mb(addr, skey.val); ret1.val = get_storage_key(end - PAGE_SIZE) & (SKEY_ACC | SKEY_FP); ret2.val = get_storage_key(end - PAGE_SIZE * 2) & (SKEY_ACC | SKEY_FP); report(ret1.val == ret2.val && ret1.val == skey.val, "multi block"); } static void test_chg(void) { union skey skey1, skey2; skey1.val = 0x30; set_storage_key(pagebuf, skey1.val, 0); skey1.val = get_storage_key(pagebuf); pagebuf[0] = 3; skey2.val = get_storage_key(pagebuf); report(!skey1.str.ch && skey2.str.ch, "chg bit test"); } static void test_set(void) { union skey skey, ret; skey.val = 0x30; ret.val = get_storage_key(pagebuf); set_storage_key(pagebuf, skey.val, 0); ret.val = get_storage_key(pagebuf); /* * For all set tests we only test the ACC and FP bits. RF and * CH are set by the machine for memory references and changes * and hence might change between a set and a get. */ report(skey.str.acc == ret.str.acc && skey.str.fp == ret.str.fp, "set key test"); } static void test_priv(void) { union skey skey; memset(pagebuf, 0, PAGE_SIZE * 2); report_prefix_push("privileged"); report_prefix_push("sske"); expect_pgm_int(); enter_pstate(); set_storage_key(pagebuf, 0x30, 0); check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); report_prefix_pop(); skey.val = get_storage_key(pagebuf); report(skey.str.acc != 3, "skey did not change on exception"); report_prefix_push("iske"); expect_pgm_int(); enter_pstate(); get_storage_key(pagebuf); check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); report_prefix_pop(); report_prefix_pop(); } static void test_invalid_address(void) { void *inv_addr = (void *)-1ull; report_prefix_push("invalid address"); report_prefix_push("sske"); expect_pgm_int(); set_storage_key(inv_addr, 0, 0); check_pgm_int_code(PGM_INT_CODE_ADDRESSING); report_prefix_pop(); report_prefix_push("iske"); expect_pgm_int(); get_storage_key(inv_addr); check_pgm_int_code(PGM_INT_CODE_ADDRESSING); report_prefix_pop(); report_prefix_push("rrbe"); expect_pgm_int(); reset_reference_bit(inv_addr); check_pgm_int_code(PGM_INT_CODE_ADDRESSING); report_prefix_pop(); report_prefix_pop(); } static void test_test_protection(void) { unsigned long addr = (unsigned long)pagebuf; report_prefix_push("TPROT"); set_storage_key(pagebuf, 0x10, 0); report(tprot(addr, 0) == TPROT_READ_WRITE, "zero key: no protection"); report(tprot(addr, 1) == TPROT_READ_WRITE, "matching key: no protection"); report_prefix_push("mismatching key"); report(tprot(addr, 2) == TPROT_READ, "no fetch protection: store protection"); set_storage_key(pagebuf, 0x18, 0); report(tprot(addr, 2) == TPROT_RW_PROTECTED, "fetch protection: fetch & store protection"); report_prefix_push("fetch-protection override"); set_storage_key(0, 0x18, 0); report(tprot(0, 2) == TPROT_RW_PROTECTED, "disabled: fetch & store protection"); ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE); report(tprot(0, 2) == TPROT_READ, "enabled: store protection"); report(tprot(2048, 2) == TPROT_RW_PROTECTED, "invalid: fetch & store protection"); ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE); set_storage_key(0, 0x00, 0); report_prefix_pop(); ctl_set_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE); set_storage_key(pagebuf, 0x90, 0); report(tprot(addr, 2) == TPROT_READ_WRITE, "storage-protection override: no protection"); ctl_clear_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE); report_prefix_pop(); set_storage_key(pagebuf, 0x00, 0); report_prefix_pop(); } enum access { ACC_STORE = 1, ACC_FETCH = 2, ACC_UPDATE = 3, }; enum protection { PROT_STORE = 1, PROT_FETCH_STORE = 3, }; static void check_key_prot_exc(enum access access, enum protection prot) { union teid teid; int access_code; check_pgm_int_code(PGM_INT_CODE_PROTECTION); report_prefix_push("TEID"); teid.val = lowcore.trans_exc_id; switch (get_supp_on_prot_facility()) { case SOP_NONE: case SOP_BASIC: /* let's ignore ancient/irrelevant machines */ break; case SOP_ENHANCED_1: report(!teid.sop_teid_predictable, "valid protection code"); /* no access code in case of key protection */ break; case SOP_ENHANCED_2: switch (teid_esop2_prot_code(teid)) { case PROT_KEY: /* ESOP-2: no need to check facility */ access_code = teid.acc_exc_fetch_store; switch (access_code) { case 0: report_pass("valid access code"); break; case 1: case 2: report((access & access_code) && (prot & access_code), "valid access code"); break; case 3: /* * This is incorrect in that reserved values * should be ignored, but kvm should not return * a reserved value and having a test for that * is more valuable. */ report_fail("valid access code"); break; } /* fallthrough */ case PROT_KEY_OR_LAP: report_pass("valid protection code"); break; default: report_fail("valid protection code"); } break; } report_prefix_pop(); } /* * Perform STORE CPU ADDRESS (STAP) instruction while temporarily executing * with access key 1. */ static void store_cpu_address_key_1(uint16_t *out) { asm volatile ( "spka 0x10\n\t" "stap %0\n\t" "spka 0\n" : "+Q" (*out) /* exception: old value remains in out -> + constraint */ ); } static void test_store_cpu_address(void) { uint16_t *out = (uint16_t *)pagebuf; uint16_t cpu_addr; report_prefix_push("STORE CPU ADDRESS"); asm ("stap %0" : "=Q" (cpu_addr)); report_prefix_push("zero key"); set_storage_key(pagebuf, 0x20, 0); WRITE_ONCE(*out, 0xbeef); asm ("stap %0" : "=Q" (*out)); report(*out == cpu_addr, "store occurred"); report_prefix_pop(); report_prefix_push("matching key"); set_storage_key(pagebuf, 0x10, 0); *out = 0xbeef; store_cpu_address_key_1(out); report(*out == cpu_addr, "store occurred"); report_prefix_pop(); report_prefix_push("mismatching key"); set_storage_key(pagebuf, 0x20, 0); expect_pgm_int(); *out = 0xbeef; store_cpu_address_key_1(out); check_key_prot_exc(ACC_STORE, PROT_STORE); report(*out == 0xbeef, "no store occurred"); report_prefix_pop(); ctl_set_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE); report_prefix_push("storage-protection override, invalid key"); set_storage_key(pagebuf, 0x20, 0); expect_pgm_int(); *out = 0xbeef; store_cpu_address_key_1(out); check_key_prot_exc(ACC_STORE, PROT_STORE); report(*out == 0xbeef, "no store occurred"); report_prefix_pop(); report_prefix_push("storage-protection override, override key"); set_storage_key(pagebuf, 0x90, 0); *out = 0xbeef; store_cpu_address_key_1(out); report(*out == cpu_addr, "override occurred"); report_prefix_pop(); ctl_clear_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE); report_prefix_push("storage-protection override disabled, override key"); set_storage_key(pagebuf, 0x90, 0); expect_pgm_int(); *out = 0xbeef; store_cpu_address_key_1(out); check_key_prot_exc(ACC_STORE, PROT_STORE); report(*out == 0xbeef, "no store occurred"); report_prefix_pop(); set_storage_key(pagebuf, 0x00, 0); report_prefix_pop(); } static void test_diag_308(void) { uint16_t response; uint32_t *ipib = (uint32_t *)pagebuf; report_prefix_push("DIAG 308"); WRITE_ONCE(ipib[0], 0); /* Invalid length */ set_storage_key(ipib, 0x28, 0); /* key-controlled protection does not apply */ asm volatile ( "lr %%r2,%[ipib]\n\t" "spka 0x10\n\t" "diag %%r2,%[code],0x308\n\t" "spka 0\n\t" "lr %[response],%%r3\n" : [response] "=d" (response) : [ipib] "d" (ipib), [code] "d" (5L) : "%r2", "%r3" ); report(response == 0x402, "no exception on fetch, response: invalid IPIB"); set_storage_key(ipib, 0x00, 0); report_prefix_pop(); } /* * Perform CHANNEL SUBSYSTEM CALL (CHSC) instruction while temporarily executing * with access key 1. */ static unsigned int chsc_key_1(void *comm_block) { uint32_t program_mask; asm volatile ( "spka 0x10\n\t" ".insn rre,0xb25f0000,%[comm_block],0\n\t" "spka 0\n\t" "ipm %[program_mask]\n" : [program_mask] "=d" (program_mask) : [comm_block] "d" (comm_block) : "memory" ); return program_mask >> 28; } static const char chsc_msg[] = "Performed store-channel-subsystem-characteristics"; static void init_comm_block(uint16_t *comm_block) { memset(comm_block, 0, PAGE_SIZE); /* store-channel-subsystem-characteristics command */ comm_block[0] = 0x10; comm_block[1] = 0x10; comm_block[9] = 0; } static void test_channel_subsystem_call(void) { uint16_t *comm_block = (uint16_t *)&pagebuf; unsigned int cc; report_prefix_push("CHANNEL SUBSYSTEM CALL"); report_prefix_push("zero key"); init_comm_block(comm_block); set_storage_key(comm_block, 0x10, 0); asm volatile ( ".insn rre,0xb25f0000,%[comm_block],0\n\t" "ipm %[cc]\n" : [cc] "=d" (cc) : [comm_block] "d" (comm_block) : "memory" ); cc = cc >> 28; report(cc == 0 && comm_block[9], chsc_msg); report_prefix_pop(); report_prefix_push("matching key"); init_comm_block(comm_block); set_storage_key(comm_block, 0x10, 0); cc = chsc_key_1(comm_block); report(cc == 0 && comm_block[9], chsc_msg); report_prefix_pop(); report_prefix_push("mismatching key"); report_prefix_push("no fetch protection"); init_comm_block(comm_block); set_storage_key(comm_block, 0x20, 0); expect_pgm_int(); chsc_key_1(comm_block); check_key_prot_exc(ACC_UPDATE, PROT_STORE); report_prefix_pop(); report_prefix_push("fetch protection"); init_comm_block(comm_block); set_storage_key(comm_block, 0x28, 0); expect_pgm_int(); chsc_key_1(comm_block); check_key_prot_exc(ACC_UPDATE, PROT_FETCH_STORE); report_prefix_pop(); ctl_set_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE); report_prefix_push("storage-protection override, invalid key"); set_storage_key(comm_block, 0x20, 0); init_comm_block(comm_block); expect_pgm_int(); chsc_key_1(comm_block); check_key_prot_exc(ACC_UPDATE, PROT_STORE); report_prefix_pop(); report_prefix_push("storage-protection override, override key"); init_comm_block(comm_block); set_storage_key(comm_block, 0x90, 0); cc = chsc_key_1(comm_block); report(cc == 0 && comm_block[9], chsc_msg); report_prefix_pop(); ctl_clear_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE); report_prefix_push("storage-protection override disabled, override key"); init_comm_block(comm_block); set_storage_key(comm_block, 0x90, 0); expect_pgm_int(); chsc_key_1(comm_block); check_key_prot_exc(ACC_UPDATE, PROT_STORE); report_prefix_pop(); report_prefix_pop(); set_storage_key(comm_block, 0x00, 0); report_prefix_pop(); } /* * Perform SET PREFIX (SPX) instruction while temporarily executing * with access key 1. */ static void set_prefix_key_1(uint32_t *prefix_ptr) { asm volatile ( "spka 0x10\n\t" "spx %0\n\t" "spka 0\n" :: "Q" (*prefix_ptr) ); } #define PREFIX_AREA_SIZE (PAGE_SIZE * 2) static char lowcore_tmp[PREFIX_AREA_SIZE] __attribute__((aligned(PREFIX_AREA_SIZE))); /* * Test accessibility of the operand to SET PREFIX given different configurations * with regards to storage keys. That is, check the accessibility of the location * holding the new prefix, not that of the new prefix area. The new prefix area * is a valid lowcore, so that the test does not crash on failure. */ static void test_set_prefix(void) { uint32_t *prefix_ptr = (uint32_t *)pagebuf; uint32_t *no_override_prefix_ptr; uint32_t old_prefix; pgd_t *root; report_prefix_push("SET PREFIX"); root = (pgd_t *)(stctg(1) & PAGE_MASK); old_prefix = get_prefix(); memcpy(lowcore_tmp, 0, sizeof(lowcore_tmp)); assert(((uint64_t)&lowcore_tmp >> 31) == 0); *prefix_ptr = (uint32_t)(uint64_t)&lowcore_tmp; report_prefix_push("zero key"); set_prefix(old_prefix); set_storage_key(prefix_ptr, 0x20, 0); set_prefix(*prefix_ptr); report(get_prefix() == *prefix_ptr, "set prefix"); report_prefix_pop(); report_prefix_push("matching key"); set_prefix(old_prefix); set_storage_key(pagebuf, 0x10, 0); set_prefix_key_1(prefix_ptr); report(get_prefix() == *prefix_ptr, "set prefix"); report_prefix_pop(); report_prefix_push("mismatching key"); report_prefix_push("no fetch protection"); set_prefix(old_prefix); set_storage_key(pagebuf, 0x20, 0); set_prefix_key_1(prefix_ptr); report(get_prefix() == *prefix_ptr, "set prefix"); report_prefix_pop(); report_prefix_push("fetch protection"); set_prefix(old_prefix); set_storage_key(pagebuf, 0x28, 0); expect_pgm_int(); set_prefix_key_1(prefix_ptr); check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE); report(get_prefix() == old_prefix, "did not set prefix"); report_prefix_pop(); /* * Page 0 will be remapped, making the lowcore inaccessible, which * breaks the normal handler and breaks skipping the faulting * instruction. Disable dynamic address translation for the * interrupt handler to make things work. */ lowcore.pgm_new_psw.mask &= ~PSW_MASK_DAT; report_prefix_push("remapped page, fetch protection"); set_prefix(old_prefix); set_storage_key(pagebuf, 0x28, 0); expect_pgm_int(); install_page(root, virt_to_pte_phys(root, pagebuf), 0); set_prefix_key_1((uint32_t *)0); install_page(root, 0, 0); check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE); report(get_prefix() == old_prefix, "did not set prefix"); report_prefix_pop(); ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE); report_prefix_push("fetch protection override applies"); set_prefix(old_prefix); set_storage_key(pagebuf, 0x28, 0); install_page(root, virt_to_pte_phys(root, pagebuf), 0); set_prefix_key_1((uint32_t *)0); install_page(root, 0, 0); report(get_prefix() == *prefix_ptr, "set prefix"); report_prefix_pop(); no_override_prefix_ptr = (uint32_t *)(pagebuf + 2048); WRITE_ONCE(*no_override_prefix_ptr, (uint32_t)(uint64_t)&lowcore_tmp); report_prefix_push("fetch protection override does not apply"); set_prefix(old_prefix); set_storage_key(pagebuf, 0x28, 0); expect_pgm_int(); install_page(root, virt_to_pte_phys(root, pagebuf), 0); set_prefix_key_1(OPAQUE_PTR(2048)); install_page(root, 0, 0); check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE); report(get_prefix() == old_prefix, "did not set prefix"); report_prefix_pop(); ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE); lowcore.pgm_new_psw.mask |= PSW_MASK_DAT; report_prefix_pop(); set_storage_key(pagebuf, 0x00, 0); report_prefix_pop(); } /* * Perform MODIFY SUBCHANNEL (MSCH) instruction while temporarily executing * with access key 1. */ static uint32_t modify_subchannel_key_1(uint32_t sid, struct schib *schib) { uint32_t program_mask; asm volatile ( "lr %%r1,%[sid]\n\t" "spka 0x10\n\t" "msch %[schib]\n\t" "spka 0\n\t" "ipm %[program_mask]\n" : [program_mask] "=d" (program_mask) : [sid] "d" (sid), [schib] "Q" (*schib) : "%r1" ); return program_mask >> 28; } static void test_msch(void) { struct schib *schib = (struct schib *)pagebuf; struct schib *no_override_schib; int test_device_sid; pgd_t *root; int cc; report_prefix_push("MSCH"); root = (pgd_t *)(stctg(1) & PAGE_MASK); test_device_sid = css_enumerate(); if (!(test_device_sid & SCHID_ONE)) { report_fail("no I/O device found"); return; } cc = stsch(test_device_sid, schib); if (cc) { report_fail("could not store SCHIB"); return; } report_prefix_push("zero key"); schib->pmcw.intparm = 100; set_storage_key(schib, 0x28, 0); cc = msch(test_device_sid, schib); if (!cc) { WRITE_ONCE(schib->pmcw.intparm, 0); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 100, "fetched from SCHIB"); } else { report_fail("MSCH cc != 0"); } report_prefix_pop(); report_prefix_push("matching key"); schib->pmcw.intparm = 200; set_storage_key(schib, 0x18, 0); cc = modify_subchannel_key_1(test_device_sid, schib); if (!cc) { WRITE_ONCE(schib->pmcw.intparm, 0); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 200, "fetched from SCHIB"); } else { report_fail("MSCH cc != 0"); } report_prefix_pop(); report_prefix_push("mismatching key"); report_prefix_push("no fetch protection"); schib->pmcw.intparm = 300; set_storage_key(schib, 0x20, 0); cc = modify_subchannel_key_1(test_device_sid, schib); if (!cc) { WRITE_ONCE(schib->pmcw.intparm, 0); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 300, "fetched from SCHIB"); } else { report_fail("MSCH cc != 0"); } report_prefix_pop(); schib->pmcw.intparm = 0; if (!msch(test_device_sid, schib)) { report_prefix_push("fetch protection"); schib->pmcw.intparm = 400; set_storage_key(schib, 0x28, 0); expect_pgm_int(); modify_subchannel_key_1(test_device_sid, schib); check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel"); report_prefix_pop(); } else { report_fail("could not reset SCHIB"); } /* * Page 0 will be remapped, making the lowcore inaccessible, which * breaks the normal handler and breaks skipping the faulting * instruction. Disable dynamic address translation for the * interrupt handler to make things work. */ lowcore.pgm_new_psw.mask &= ~PSW_MASK_DAT; schib->pmcw.intparm = 0; if (!msch(test_device_sid, schib)) { report_prefix_push("remapped page, fetch protection"); schib->pmcw.intparm = 500; set_storage_key(pagebuf, 0x28, 0); expect_pgm_int(); install_page(root, virt_to_pte_phys(root, pagebuf), 0); modify_subchannel_key_1(test_device_sid, (struct schib *)0); install_page(root, 0, 0); check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel"); report_prefix_pop(); } else { report_fail("could not reset SCHIB"); } ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE); report_prefix_push("fetch-protection override applies"); schib->pmcw.intparm = 600; set_storage_key(pagebuf, 0x28, 0); install_page(root, virt_to_pte_phys(root, pagebuf), 0); cc = modify_subchannel_key_1(test_device_sid, (struct schib *)0); install_page(root, 0, 0); if (!cc) { WRITE_ONCE(schib->pmcw.intparm, 0); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 600, "fetched from SCHIB"); } else { report_fail("MSCH cc != 0"); } report_prefix_pop(); schib->pmcw.intparm = 0; if (!msch(test_device_sid, schib)) { report_prefix_push("fetch-protection override does not apply"); schib->pmcw.intparm = 700; no_override_schib = (struct schib *)(pagebuf + 2048); memcpy(no_override_schib, schib, sizeof(struct schib)); set_storage_key(pagebuf, 0x28, 0); expect_pgm_int(); install_page(root, virt_to_pte_phys(root, pagebuf), 0); modify_subchannel_key_1(test_device_sid, OPAQUE_PTR(2048)); install_page(root, 0, 0); check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE); cc = stsch(test_device_sid, schib); report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel"); report_prefix_pop(); } else { report_fail("could not reset SCHIB"); } ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE); lowcore.pgm_new_psw.mask |= PSW_MASK_DAT; report_prefix_pop(); set_storage_key(schib, 0x00, 0); report_prefix_pop(); } int main(void) { report_prefix_push("skey"); if (test_facility(169)) { report_skip("storage key removal facility is active"); goto done; } test_priv(); test_invalid_address(); test_set(); test_set_mb(); test_chg(); test_test_protection(); test_store_cpu_address(); test_diag_308(); test_channel_subsystem_call(); setup_vm(); test_set_prefix(); test_msch(); done: report_prefix_pop(); return report_summary(); }