blob: 55b46446593325459f416de3b3a1a361f9eaedc5 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Host Ultravisor Call tests
*
* Copyright (c) 2021 IBM Corp
*
* Authors:
* Janosch Frank <frankja@linux.ibm.com>
*/
#include <libcflat.h>
#include <hardware.h>
#include <alloc.h>
#include <vmalloc.h>
#include <sclp.h>
#include <smp.h>
#include <uv.h>
#include <snippet.h>
#include <mmu.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/asm-offsets.h>
#include <asm/interrupt.h>
#include <asm/facility.h>
#include <asm/pgtable.h>
#include <asm/uv.h>
#include <asm-generic/barrier.h>
static struct uv_cb_qui uvcb_qui;
static struct uv_cb_init uvcb_init;
static struct uv_cb_cgc uvcb_cgc;
static struct uv_cb_csc uvcb_csc;
extern int diag308_load_reset(u64 code);
struct cmd_list {
const char *name;
uint16_t cmd;
uint16_t len;
int call_bit;
};
static void cpu_loop(void)
{
for (;;) {}
}
/*
* Checks if a memory area is protected as secure memory.
* Will return true if all pages are protected, false otherwise.
*/
static bool access_check_3d(uint8_t *access_ptr, uint64_t len)
{
assert(!(len & ~PAGE_MASK));
assert(!((uint64_t)access_ptr & ~PAGE_MASK));
while (len) {
expect_pgm_int();
READ_ONCE(*access_ptr);
if (clear_pgm_int() != PGM_INT_CODE_SECURE_STOR_ACCESS)
return false;
expect_pgm_int();
WRITE_ONCE(*access_ptr, 42);
if (clear_pgm_int() != PGM_INT_CODE_SECURE_STOR_ACCESS)
return false;
access_ptr += PAGE_SIZE;
len -= PAGE_SIZE;
}
return true;
}
static struct cmd_list cmds[] = {
{ "init", UVC_CMD_INIT_UV, sizeof(struct uv_cb_init), BIT_UVC_CMD_INIT_UV },
{ "create conf", UVC_CMD_CREATE_SEC_CONF, sizeof(struct uv_cb_cgc), BIT_UVC_CMD_CREATE_SEC_CONF },
{ "destroy conf", UVC_CMD_DESTROY_SEC_CONF, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_DESTROY_SEC_CONF },
{ "create cpu", UVC_CMD_CREATE_SEC_CPU, sizeof(struct uv_cb_csc), BIT_UVC_CMD_CREATE_SEC_CPU },
{ "destroy cpu", UVC_CMD_DESTROY_SEC_CPU, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_DESTROY_SEC_CPU },
{ "conv to", UVC_CMD_CONV_TO_SEC_STOR, sizeof(struct uv_cb_cts), BIT_UVC_CMD_CONV_TO_SEC_STOR },
{ "conv from", UVC_CMD_CONV_FROM_SEC_STOR, sizeof(struct uv_cb_cfs), BIT_UVC_CMD_CONV_FROM_SEC_STOR },
{ "set sec conf", UVC_CMD_SET_SEC_CONF_PARAMS, sizeof(struct uv_cb_ssc), BIT_UVC_CMD_SET_SEC_PARMS },
{ "unpack", UVC_CMD_UNPACK_IMG, sizeof(struct uv_cb_unp), BIT_UVC_CMD_UNPACK_IMG },
{ "verify", UVC_CMD_VERIFY_IMG, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_VERIFY_IMG },
{ "cpu reset", UVC_CMD_CPU_RESET, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_CPU_RESET },
{ "cpu initial reset", UVC_CMD_CPU_RESET_INITIAL, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_CPU_RESET_INITIAL },
{ "conf clear reset", UVC_CMD_PREPARE_RESET, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_PREPARE_RESET },
{ "cpu clear reset", UVC_CMD_CPU_RESET_CLEAR, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_CPU_PERFORM_CLEAR_RESET },
{ "cpu set state", UVC_CMD_CPU_SET_STATE, sizeof(struct uv_cb_cpu_set_state), BIT_UVC_CMD_CPU_SET_STATE },
{ "pin shared", UVC_CMD_PIN_PAGE_SHARED, sizeof(struct uv_cb_cfs), BIT_UVC_CMD_PIN_PAGE_SHARED },
{ "unpin shared", UVC_CMD_UNPIN_PAGE_SHARED, sizeof(struct uv_cb_cts), BIT_UVC_CMD_UNPIN_PAGE_SHARED },
{ NULL, 0, 0 },
};
static void test_i3(void)
{
struct uv_cb_header uvcb = {
.cmd = UVC_CMD_INIT_UV,
.len = sizeof(struct uv_cb_init),
};
unsigned long r1 = 0;
int cc;
report_prefix_push("i3");
expect_pgm_int();
asm volatile(
"0: .insn rrf,0xB9A40000,%[r1],%[r2],4,2\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc)
: [r1] "a" (r1), [r2] "a" (&uvcb)
: "memory", "cc");
check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
report_prefix_pop();
}
static void test_priv(void)
{
struct uv_cb_header uvcb = {};
uint16_t pgm;
int i;
report_prefix_push("privileged");
for (i = 0; cmds[i].name; i++) {
expect_pgm_int();
uvcb.cmd = cmds[i].cmd;
uvcb.len = cmds[i].len;
enter_pstate();
uv_call_once(0, (uint64_t)&uvcb);
pgm = clear_pgm_int();
report(pgm == PGM_INT_CODE_PRIVILEGED_OPERATION, "%s", cmds[i].name);
}
report_prefix_pop();
}
static void test_uv_uninitialized(void)
{
struct uv_cb_header uvcb = {};
int i;
report_prefix_push("uninitialized");
for (i = 0; cmds[i].name; i++) {
if (cmds[i].cmd == UVC_CMD_INIT_UV)
continue;
expect_pgm_int();
uvcb.cmd = cmds[i].cmd;
uvcb.len = cmds[i].len;
uv_call_once(0, (uint64_t)&uvcb);
report(uvcb.rc == UVC_RC_INV_STATE, "%s", cmds[i].name);
}
report_prefix_pop();
}
static void test_access(void)
{
struct uv_cb_header *uvcb;
void *pages = alloc_pages(1);
uint16_t pgm;
int i;
/* Put UVCB on second page which we will protect later */
uvcb = pages + PAGE_SIZE;
report_prefix_push("access");
/*
* If debug is enabled info from the uv header is printed
* which would lead to a second exception and a test abort.
*/
if (UVC_ERR_DEBUG) {
report_skip("Debug doesn't work with access tests");
goto out;
}
report_prefix_push("non-crossing");
protect_page(uvcb, PAGE_ENTRY_I);
for (i = 0; cmds[i].name; i++) {
expect_pgm_int();
mb();
uv_call_once(0, (uint64_t)uvcb);
pgm = clear_pgm_int();
report(pgm == PGM_INT_CODE_PAGE_TRANSLATION, "%s", cmds[i].name);
}
report_prefix_pop();
report_prefix_push("crossing");
/*
* Put the header into the readable page 1, everything after
* the header will be on the second, invalid page.
*/
uvcb -= 1;
for (i = 0; cmds[i].name; i++) {
uvcb->cmd = cmds[i].cmd;
uvcb->len = cmds[i].len;
expect_pgm_int();
mb();
uv_call_once(0, (uint64_t)uvcb);
pgm = clear_pgm_int();
report(pgm == PGM_INT_CODE_PAGE_TRANSLATION, "%s", cmds[i].name);
}
report_prefix_pop();
uvcb += 1;
unprotect_page(uvcb, PAGE_ENTRY_I);
out:
free_pages(pages);
report_prefix_pop();
}
static void test_config_destroy(void)
{
int rc;
struct uv_cb_nodata uvcb = {
.header.cmd = UVC_CMD_DESTROY_SEC_CONF,
.header.len = sizeof(uvcb),
.handle = uvcb_cgc.guest_handle,
};
report_prefix_push("dsc");
uvcb.header.len -= 8;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == UVC_RC_INV_LEN,
"hdr invalid length");
uvcb.header.len += 8;
uvcb.handle += 1;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == UVC_RC_INV_GHANDLE, "invalid handle");
uvcb.handle -= 1;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 0 && uvcb.header.rc == UVC_RC_EXECUTED, "success");
report_prefix_pop();
}
static void test_cpu_destroy(void)
{
int rc;
struct uv_cb_nodata uvcb = {
.header.len = sizeof(uvcb),
.header.cmd = UVC_CMD_DESTROY_SEC_CPU,
.handle = uvcb_csc.cpu_handle,
};
report_prefix_push("dcpu");
uvcb.header.len -= 8;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == UVC_RC_INV_LEN,
"hdr invalid length");
uvcb.header.len += 8;
if (!machine_is_z15()) {
uvcb.handle += 1;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == UVC_RC_INV_CHANDLE, "invalid handle");
uvcb.handle -= 1;
}
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 0 && uvcb.header.rc == UVC_RC_EXECUTED, "success");
report_prefix_pop();
}
static void test_set_se_header(void)
{
struct uv_cb_ssc uvcb = {
.header.cmd = UVC_CMD_SET_SEC_CONF_PARAMS,
.header.len = sizeof(uvcb),
.guest_handle = uvcb_cgc.guest_handle,
.sec_header_origin = 0,
.sec_header_len = 0x1000,
};
void *pages = alloc_pages(1);
void *inv;
int rc;
report_prefix_push("sscp");
uvcb.header.len -= 8;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == UVC_RC_INV_LEN,
"hdr invalid length");
uvcb.header.len += 8;
uvcb.guest_handle += 1;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == UVC_RC_INV_GHANDLE, "invalid handle");
uvcb.guest_handle -= 1;
inv = pages + PAGE_SIZE;
uvcb.sec_header_origin = (uint64_t)inv;
protect_page(inv, PAGE_ENTRY_I);
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == 0x103,
"se hdr access exception");
/*
* Shift the ptr so the first few DWORDs are accessible but
* the following are on an invalid page.
*/
uvcb.sec_header_origin -= 0x20;
rc = uv_call(0, (uint64_t)&uvcb);
report(rc == 1 && uvcb.header.rc == 0x103,
"se hdr access exception crossing");
unprotect_page(inv, PAGE_ENTRY_I);
free_pages(pages);
report_prefix_pop();
}
static void test_cpu_create(void)
{
int rc;
unsigned long tmp;
report_prefix_push("csc");
uvcb_csc.header.len = sizeof(uvcb_csc);
uvcb_csc.header.cmd = UVC_CMD_CREATE_SEC_CPU;
uvcb_csc.guest_handle = uvcb_cgc.guest_handle;
uvcb_csc.stor_origin = (unsigned long)memalign(PAGE_SIZE, uvcb_qui.cpu_stor_len);
uvcb_csc.state_origin = (unsigned long)memalign(PAGE_SIZE, PAGE_SIZE);
uvcb_csc.header.len -= 8;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(uvcb_csc.header.rc == UVC_RC_INV_LEN && rc == 1 &&
!uvcb_csc.cpu_handle, "hdr invalid length");
uvcb_csc.header.len += 8;
uvcb_csc.guest_handle += 1;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(uvcb_csc.header.rc == UVC_RC_INV_GHANDLE && rc == 1,
"invalid guest handle");
uvcb_csc.guest_handle -= 1;
uvcb_csc.num = uvcb_qui.max_guest_cpus + 1;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(uvcb_csc.header.rc == 0x103 && rc == 1,
"invalid cpu #");
uvcb_csc.num = 0;
tmp = uvcb_csc.stor_origin;
uvcb_csc.stor_origin = get_max_ram_size() + PAGE_SIZE;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(uvcb_csc.header.rc == 0x105 && rc == 1,
"cpu stor inaccessible");
uvcb_csc.stor_origin = tmp;
tmp = uvcb_csc.stor_origin;
uvcb_csc.stor_origin = 0;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(uvcb_csc.header.rc == 0x106 && rc == 1,
"cpu stor in lowcore");
uvcb_csc.stor_origin = tmp;
tmp = uvcb_csc.state_origin;
uvcb_csc.state_origin = get_max_ram_size() + PAGE_SIZE;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(uvcb_csc.header.rc == 0x107 && rc == 1,
"SIE SD inaccessible");
uvcb_csc.state_origin = tmp;
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(rc == 0 && uvcb_csc.header.rc == UVC_RC_EXECUTED &&
uvcb_csc.cpu_handle, "success");
rc = access_check_3d((uint8_t *)uvcb_csc.stor_origin,
uvcb_qui.cpu_stor_len);
report(rc, "Storage protection");
tmp = uvcb_csc.stor_origin;
uvcb_csc.stor_origin = (unsigned long)memalign(PAGE_SIZE, uvcb_qui.cpu_stor_len);
rc = uv_call(0, (uint64_t)&uvcb_csc);
report(rc == 1 && uvcb_csc.header.rc == 0x104, "already defined");
uvcb_csc.stor_origin = tmp;
report_prefix_pop();
}
/*
* If the first bit of the rc is set we need to destroy the
* configuration before testing other create config errors.
*/
static void cgc_destroy_if_needed(struct uv_cb_cgc *uvcb)
{
uint16_t rc, rrc;
if (uvcb->header.rc != UVC_RC_EXECUTED &&
!(uvcb->header.rc & UVC_RC_DSTR_NEEDED_FLG))
return;
assert(uvcb->guest_handle);
assert(!uv_cmd_nodata(uvcb->guest_handle, UVC_CMD_DESTROY_SEC_CONF,
&rc, &rrc));
/* We need to zero it for the next test */
uvcb->guest_handle = 0;
}
static bool cgc_check_data(struct uv_cb_cgc *uvcb, uint16_t rc_expected)
{
/* This function purely checks for error rcs */
if (uvcb->header.rc == UVC_RC_EXECUTED)
return false;
/*
* We should only receive a handle when the rc is 1 or the
* first bit is set.
*/
if (!(uvcb->header.rc & UVC_RC_DSTR_NEEDED_FLG) && uvcb->guest_handle)
report_abort("Received a handle when we didn't expect one");
return (uvcb->header.rc & ~UVC_RC_DSTR_NEEDED_FLG) == rc_expected;
}
static void test_config_create(void)
{
int rc;
unsigned long vsize, tmp;
static struct uv_cb_cgc uvcb;
uvcb_cgc.header.cmd = UVC_CMD_CREATE_SEC_CONF;
uvcb_cgc.header.len = sizeof(uvcb_cgc);
report_prefix_push("cgc");
uvcb_cgc.guest_stor_origin = 0;
uvcb_cgc.guest_stor_len = 42 * (1UL << 20);
vsize = uvcb_qui.conf_base_virt_stor_len +
((uvcb_cgc.guest_stor_len / (1UL << 20)) * uvcb_qui.conf_virt_var_stor_len);
uvcb_cgc.conf_base_stor_origin = (uint64_t)memalign(PAGE_SIZE * 4, uvcb_qui.conf_base_phys_stor_len);
uvcb_cgc.conf_var_stor_origin = (uint64_t)memalign(PAGE_SIZE, vsize);
uvcb_cgc.guest_asce = (uint64_t)memalign(PAGE_SIZE, 4 * PAGE_SIZE) | ASCE_DT_SEGMENT | REGION_TABLE_LENGTH | ASCE_P;
uvcb_cgc.guest_sca = (uint64_t)memalign(PAGE_SIZE * 4, PAGE_SIZE * 4);
uvcb_cgc.header.len -= 8;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(uvcb_cgc.header.rc == UVC_RC_INV_LEN && rc == 1 &&
!uvcb_cgc.guest_handle, "hdr invalid length");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.header.len += 8;
uvcb_cgc.guest_stor_origin = uvcb_qui.max_guest_stor_addr + (1UL << 20) * 2 + 1;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x101) && rc == 1,
"MSO > max guest addr");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_stor_origin = 0;
uvcb_cgc.guest_stor_origin = uvcb_qui.max_guest_stor_addr - (1UL << 20);
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x102) && rc == 1,
"MSO + MSL > max guest addr");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_stor_origin = 0;
uvcb_cgc.guest_asce &= ~ASCE_P;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x105) && rc == 1,
"ASCE private bit missing");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_asce |= ASCE_P;
uvcb_cgc.guest_asce |= 0x20;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x105) && rc == 1,
"ASCE bit 58 set");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_asce &= ~0x20;
tmp = uvcb_cgc.conf_base_stor_origin;
uvcb_cgc.conf_base_stor_origin = get_max_ram_size() + 8;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x108) && rc == 1,
"base storage origin > available memory");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.conf_base_stor_origin = tmp;
tmp = uvcb_cgc.conf_base_stor_origin;
uvcb_cgc.conf_base_stor_origin = 0x1000;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x109) && rc == 1,
"base storage origin contains lowcore %x", uvcb_cgc.header.rc);
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.conf_base_stor_origin = tmp;
tmp = uvcb_cgc.guest_sca;
uvcb_cgc.guest_sca = 0;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x10c) && rc == 1,
"sca == 0");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_sca = tmp;
tmp = uvcb_cgc.guest_sca;
uvcb_cgc.guest_sca = get_max_ram_size() + PAGE_SIZE * 4;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(cgc_check_data(&uvcb_cgc, 0x10d) && rc == 1,
"sca inaccessible");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_sca = tmp;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(rc == 0 && uvcb_cgc.header.rc == UVC_RC_EXECUTED, "successful");
rc = access_check_3d((uint8_t *)uvcb_cgc.conf_base_stor_origin,
uvcb_qui.conf_base_phys_stor_len);
report(rc, "Base storage protection");
rc = access_check_3d((uint8_t *)uvcb_cgc.conf_var_stor_origin, vsize);
report(rc, "Variable storage protection");
uvcb_cgc.header.rc = 0;
uvcb_cgc.header.rrc = 0;
tmp = uvcb_cgc.guest_handle;
uvcb_cgc.guest_handle = 0;
rc = uv_call(0, (uint64_t)&uvcb_cgc);
report(uvcb_cgc.header.rc >= 0x100 && rc == 1, "reuse uvcb");
cgc_destroy_if_needed(&uvcb_cgc);
uvcb_cgc.guest_handle = tmp;
/* Copy over most data from uvcb_cgc, so we have the ASCE that was used. */
memcpy(&uvcb, &uvcb_cgc, sizeof(uvcb));
/* Reset the header and handle */
uvcb.header.rc = 0;
uvcb.header.rrc = 0;
uvcb.guest_handle = 0;
/* Use new storage areas. */
uvcb.conf_base_stor_origin = (uint64_t)memalign(PAGE_SIZE * 4, uvcb_qui.conf_base_phys_stor_len);
uvcb.conf_var_stor_origin = (uint64_t)memalign(PAGE_SIZE, vsize);
rc = uv_call(0, (uint64_t)&uvcb);
report(uvcb.header.rc >= 0x104 && rc == 1 && !uvcb.guest_handle,
"reuse ASCE");
cgc_destroy_if_needed(&uvcb);
free((void *)uvcb.conf_base_stor_origin);
free((void *)uvcb.conf_var_stor_origin);
/* Missing: 106, 10a, a0b */
report_prefix_pop();
}
static void test_init(void)
{
int rc;
uint64_t tmp;
/*
* Donated storage needs to be over 2GB, AREA_NORMAL does that
* on s390x.
*/
tmp = (uint64_t)memalign_pages_flags(SZ_1M, uvcb_qui.uv_base_stor_len, AREA_NORMAL);
uvcb_init.header.len = sizeof(uvcb_init);
uvcb_init.header.cmd = UVC_CMD_INIT_UV;
uvcb_init.stor_origin = tmp;
uvcb_init.stor_len = uvcb_qui.uv_base_stor_len;
report_prefix_push("init");
uvcb_init.header.len -= 8;
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && uvcb_init.header.rc == UVC_RC_INV_LEN,
"hdr invalid length");
uvcb_init.header.len += 8;
uvcb_init.stor_len -= 8;
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && uvcb_init.header.rc == 0x103,
"storage invalid length");
uvcb_init.stor_len += 8;
/* Storage origin is 1MB aligned, the length is 4KB aligned */
uvcb_init.stor_origin = get_max_ram_size();
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && (uvcb_init.header.rc == 0x104 || uvcb_init.header.rc == 0x105),
"storage origin invalid");
uvcb_init.stor_origin = tmp;
if (uvcb_init.stor_len >= HPAGE_SIZE) {
uvcb_init.stor_origin = get_max_ram_size() - HPAGE_SIZE;
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && uvcb_init.header.rc == 0x105,
"storage + length invalid");
uvcb_init.stor_origin = tmp;
} else {
report_skip("storage + length invalid, stor_len < HPAGE_SIZE");
}
uvcb_init.stor_origin = 1UL << 30;
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && uvcb_init.header.rc == 0x108,
"storage below 2GB");
uvcb_init.stor_origin = tmp;
if (smp_query_num_cpus() > 1) {
smp_cpu_setup(1, PSW_WITH_CUR_MASK(cpu_loop));
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && uvcb_init.header.rc == 0x102,
"too many running cpus");
smp_cpu_stop(1);
} else {
report_skip("Not enough cpus for 0x102 test");
}
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 0 && uvcb_init.header.rc == UVC_RC_EXECUTED, "successful");
tmp = uvcb_init.stor_origin;
uvcb_init.stor_origin = (uint64_t)memalign_pages_flags(HPAGE_SIZE, uvcb_qui.uv_base_stor_len, AREA_NORMAL);
rc = uv_call(0, (uint64_t)&uvcb_init);
report(rc == 1 && uvcb_init.header.rc == 0x101, "double init");
free((void *)uvcb_init.stor_origin);
uvcb_init.stor_origin = tmp;
report_prefix_pop();
}
static void test_query(void)
{
int i = 0, cc;
uvcb_qui.header.cmd = UVC_CMD_QUI;
uvcb_qui.header.len = sizeof(uvcb_qui);
report_prefix_push("query");
uvcb_qui.header.len = 0xa0;
uv_call(0, (uint64_t)&uvcb_qui);
report(uvcb_qui.header.rc == UVC_RC_INV_LEN, "length");
uvcb_qui.header.len = 0xa8;
uv_call(0, (uint64_t)&uvcb_qui);
report(uvcb_qui.header.rc == 0x100, "insf length");
uvcb_qui.header.len = sizeof(uvcb_qui);
cc = uv_call(0, (uint64_t)&uvcb_qui);
report((!cc && uvcb_qui.header.rc == UVC_RC_EXECUTED) ||
(cc == 1 && uvcb_qui.header.rc == 0x100),
"successful query");
for (i = 0; cmds[i].name; i++)
report(uv_query_test_call(cmds[i].call_bit), "%s", cmds[i].name);
report_prefix_pop();
}
static struct cmd_list invalid_cmds[] = {
{ "bogus", 0x4242, sizeof(struct uv_cb_header), -1},
{ "share", UVC_CMD_SET_SHARED_ACCESS, sizeof(struct uv_cb_share), BIT_UVC_CMD_SET_SHARED_ACCESS },
{ "unshare", UVC_CMD_REMOVE_SHARED_ACCESS, sizeof(struct uv_cb_share), BIT_UVC_CMD_REMOVE_SHARED_ACCESS },
{ "attest", UVC_CMD_ATTESTATION, sizeof(struct uv_cb_attest), BIT_UVC_CMD_ATTESTATION },
{ NULL, 0, 0 },
};
static void test_invalid(void)
{
struct uv_cb_header hdr = {};
int i, cc;
report_prefix_push("invalid");
for (i = 0; invalid_cmds[i].name; i++) {
hdr.cmd = invalid_cmds[i].cmd;
hdr.len = invalid_cmds[i].len;
cc = uv_call(0, (uint64_t)&hdr);
report(cc == 1 && hdr.rc == UVC_RC_INV_CMD &&
(invalid_cmds[i].call_bit == -1 || !uv_query_test_call(invalid_cmds[i].call_bit)),
"%s", invalid_cmds[i].name);
}
report_prefix_pop();
}
static void setup_test_clear(void)
{
unsigned long vsize;
int rc;
uvcb_cgc.header.cmd = UVC_CMD_CREATE_SEC_CONF;
uvcb_cgc.header.len = sizeof(uvcb_cgc);
uvcb_cgc.guest_stor_origin = 0;
uvcb_cgc.guest_stor_len = 42 * (1UL << 20);
vsize = uvcb_qui.conf_base_virt_stor_len +
((uvcb_cgc.guest_stor_len / (1UL << 20)) * uvcb_qui.conf_virt_var_stor_len);
uvcb_cgc.conf_base_stor_origin = (uint64_t)memalign(PAGE_SIZE * 4, uvcb_qui.conf_base_phys_stor_len);
uvcb_cgc.conf_var_stor_origin = (uint64_t)memalign(PAGE_SIZE, vsize);
uvcb_cgc.guest_asce = (uint64_t)memalign(PAGE_SIZE, 4 * PAGE_SIZE) | ASCE_DT_SEGMENT | REGION_TABLE_LENGTH | ASCE_P;
uvcb_cgc.guest_sca = (uint64_t)memalign(PAGE_SIZE * 4, PAGE_SIZE * 4);
rc = uv_call(0, (uint64_t)&uvcb_cgc);
assert(rc == 0);
uvcb_csc.header.len = sizeof(uvcb_csc);
uvcb_csc.header.cmd = UVC_CMD_CREATE_SEC_CPU;
uvcb_csc.guest_handle = uvcb_cgc.guest_handle;
uvcb_csc.stor_origin = (unsigned long)memalign(PAGE_SIZE, uvcb_qui.cpu_stor_len);
uvcb_csc.state_origin = (unsigned long)memalign(PAGE_SIZE, PAGE_SIZE);
rc = uv_call(0, (uint64_t)&uvcb_csc);
assert(rc == 0);
}
static void test_clear(void)
{
uint64_t *tmp;
report_prefix_push("load normal reset");
/*
* Setup a config and a cpu so we can check if a diag308 reset
* clears the donated memory and makes the pages unsecure.
*/
setup_test_clear();
diag308_load_reset(1);
sclp_console_setup();
tmp = (void *)uvcb_init.stor_origin;
report(!*tmp, "uv init donated memory cleared");
tmp = (void *)uvcb_cgc.conf_base_stor_origin;
report(!*tmp, "config base donated memory cleared");
tmp = (void *)uvcb_cgc.conf_base_stor_origin;
report(!*tmp, "config variable donated memory cleared");
tmp = (void *)uvcb_csc.stor_origin;
report(!*tmp, "cpu donated memory cleared after reset 1");
/* Check if uninitialized after reset */
test_uv_uninitialized();
report_prefix_pop();
}
static void setup_vmem(void)
{
uint64_t asce;
setup_mmu(get_max_ram_size(), NULL);
/*
* setup_mmu() will enable DAT and set the primary address
* space but we need to have a valid home space since UV calls
* take home space virtual addresses.
*
* Hence we just copy the primary asce into the home space.
*/
asce = stctg(1);
lctlg(13, asce);
}
int main(void)
{
bool has_uvc = test_facility(158);
report_prefix_push("uvc");
if (!has_uvc) {
report_skip("Ultravisor call facility is not available");
goto done;
}
if (!uv_os_is_host()) {
report_skip("This test needs to be run in a UV host environment");
goto done;
}
test_i3();
test_priv();
test_invalid();
test_uv_uninitialized();
test_query();
if (get_ram_size() < SNIPPET_PV_MIN_MEM_SIZE) {
report_skip("Not enough memory. This test needs about %ld MB of memory",
SNIPPET_PV_MIN_MEM_SIZE / SZ_1M);
goto done;
}
test_init();
setup_vmem();
test_access();
test_config_create();
test_cpu_create();
test_set_se_header();
test_cpu_destroy();
test_config_destroy();
test_clear();
done:
return report_summary();
}