@@ -12,6 +12,7 @@
#include <asm/asm-offsets.h>
#include <asm/interrupt.h>
#include <vmalloc.h>
+#include <css.h>
#include <asm/page.h>
#include <asm/facility.h>
#include <asm/mem.h>
@@ -304,6 +305,115 @@ static void test_store_cpu_address(void)
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.
@@ -430,6 +540,169 @@ static void test_set_prefix(void)
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");
+ }
+
+ register_pgm_cleanup_func(dat_fixup_pgm_int);
+
+ 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);
+ register_pgm_cleanup_func(NULL);
+ report_prefix_pop();
+ set_storage_key(schib, 0x00, 0);
+ report_prefix_pop();
+}
+
int main(void)
{
report_prefix_push("skey");
@@ -444,9 +717,11 @@ int main(void)
test_chg();
test_test_protection();
test_store_cpu_address();
+ test_channel_subsystem_call();
setup_vm();
test_set_prefix();
+ test_msch();
done:
report_prefix_pop();
return report_summary();
@@ -41,6 +41,7 @@ file = sthyi.elf
[skey]
file = skey.elf
+extra_params = -device virtio-net-ccw
[diag10]
file = diag10.elf