1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * Guest Ultravisor Call tests
4 *
5 * Copyright IBM Corp. 2020, 2022
6 *
7 * Authors:
8 * Janosch Frank <frankja@linux.ibm.com>
9 */
10
11 #include <libcflat.h>
12 #include <alloc_page.h>
13 #include <asm/page.h>
14 #include <asm/asm-offsets.h>
15 #include <asm/interrupt.h>
16 #include <asm/facility.h>
17 #include <asm/uv.h>
18 #include <sclp.h>
19 #include <uv.h>
20
21 static unsigned long page;
22
test_priv(void)23 static void test_priv(void)
24 {
25 struct uv_cb_header uvcb = {};
26
27 report_prefix_push("privileged");
28
29 report_prefix_push("query");
30 uvcb.cmd = UVC_CMD_QUI;
31 uvcb.len = sizeof(struct uv_cb_qui);
32 expect_pgm_int();
33 enter_pstate();
34 uv_call_once(0, (u64)&uvcb);
35 check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
36 report_prefix_pop();
37
38 report_prefix_push("share");
39 uvcb.cmd = UVC_CMD_SET_SHARED_ACCESS;
40 uvcb.len = sizeof(struct uv_cb_share);
41 expect_pgm_int();
42 enter_pstate();
43 uv_call_once(0, (u64)&uvcb);
44 check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
45 report_prefix_pop();
46
47 report_prefix_push("unshare");
48 uvcb.cmd = UVC_CMD_REMOVE_SHARED_ACCESS;
49 uvcb.len = sizeof(struct uv_cb_share);
50 expect_pgm_int();
51 enter_pstate();
52 uv_call_once(0, (u64)&uvcb);
53 check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
54 report_prefix_pop();
55
56 report_prefix_push("attest");
57 uvcb.cmd = UVC_CMD_ATTESTATION;
58 uvcb.len = sizeof(struct uv_cb_attest);
59 expect_pgm_int();
60 enter_pstate();
61 uv_call_once(0, (uint64_t)&uvcb);
62 check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
63 report_prefix_pop();
64
65 report_prefix_pop();
66 }
67
test_query(void)68 static void test_query(void)
69 {
70 struct uv_cb_qui uvcb = {
71 .header.cmd = UVC_CMD_QUI,
72 /* A dword below the minimum length */
73 .header.len = 0xa0,
74 };
75 int cc;
76
77 report_prefix_push("query");
78 cc = uv_call(0, (u64)&uvcb);
79 report(cc == 1 && uvcb.header.rc == UVC_RC_INV_LEN, "length");
80
81 /*
82 * BIT_UVC_CMD_QUI, BIT_UVC_CMD_SET_SHARED_ACCESS and
83 * BIT_UVC_CMD_REMOVE_SHARED_ACCESS are always present as they
84 * have been introduced with the first Ultravisor version.
85 * However, we only need to check for QUI as
86 * SET/REMOVE SHARED are used to fence this test to be only
87 * executed by protected guests.
88 */
89 report(uv_query_test_call(BIT_UVC_CMD_QUI), "query indicated");
90 report_prefix_pop();
91 }
92
test_sharing(void)93 static void test_sharing(void)
94 {
95 struct uv_cb_share uvcb = {
96 .header.cmd = UVC_CMD_SET_SHARED_ACCESS,
97 .header.len = sizeof(uvcb) - 8,
98 .paddr = page,
99 };
100 int cc;
101
102 report_prefix_push("share");
103 cc = uv_call(0, (u64)&uvcb);
104 report(cc == 1 && uvcb.header.rc == UVC_RC_INV_LEN, "length");
105 uvcb.header.len = sizeof(uvcb);
106 cc = uv_call(0, (u64)&uvcb);
107 report(cc == 0 && uvcb.header.rc == UVC_RC_EXECUTED, "share");
108 uvcb.paddr = get_ram_size() + PAGE_SIZE;
109 cc = uv_call(0, (u64)&uvcb);
110 report(cc == 1 && uvcb.header.rc == 0x101, "invalid memory");
111 uvcb.paddr = page;
112 report_prefix_pop();
113
114 report_prefix_push("unshare");
115 uvcb.header.cmd = UVC_CMD_REMOVE_SHARED_ACCESS;
116 uvcb.header.len -= 8;
117 cc = uv_call(0, (u64)&uvcb);
118 report(cc == 1 && uvcb.header.rc == UVC_RC_INV_LEN, "length");
119 uvcb.header.len = sizeof(uvcb);
120 cc = uv_call(0, (u64)&uvcb);
121 report(cc == 0 && uvcb.header.rc == UVC_RC_EXECUTED, "unshare");
122 report_prefix_pop();
123 }
124
125 static struct {
126 const char *name;
127 uint16_t cmd;
128 uint16_t len;
129 int call_bit;
130 } invalid_cmds[] = {
131 { "bogus", 0x4242, sizeof(struct uv_cb_header), -1 },
132 { "init", UVC_CMD_INIT_UV, sizeof(struct uv_cb_init), BIT_UVC_CMD_INIT_UV },
133 { "create conf", UVC_CMD_CREATE_SEC_CONF, sizeof(struct uv_cb_cgc), BIT_UVC_CMD_CREATE_SEC_CONF },
134 { "destroy conf", UVC_CMD_DESTROY_SEC_CONF, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_DESTROY_SEC_CONF },
135 { "create cpu", UVC_CMD_CREATE_SEC_CPU, sizeof(struct uv_cb_csc), BIT_UVC_CMD_CREATE_SEC_CPU },
136 { "destroy cpu", UVC_CMD_DESTROY_SEC_CPU, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_DESTROY_SEC_CPU },
137 { "conv to", UVC_CMD_CONV_TO_SEC_STOR, sizeof(struct uv_cb_cts), BIT_UVC_CMD_CONV_TO_SEC_STOR },
138 { "conv from", UVC_CMD_CONV_FROM_SEC_STOR, sizeof(struct uv_cb_cfs), BIT_UVC_CMD_CONV_FROM_SEC_STOR },
139 { "set sec conf", UVC_CMD_SET_SEC_CONF_PARAMS, sizeof(struct uv_cb_ssc), BIT_UVC_CMD_SET_SEC_PARMS },
140 { "unpack", UVC_CMD_UNPACK_IMG, sizeof(struct uv_cb_unp), BIT_UVC_CMD_UNPACK_IMG },
141 { "verify", UVC_CMD_VERIFY_IMG, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_VERIFY_IMG },
142 { "cpu reset", UVC_CMD_CPU_RESET, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_CPU_RESET },
143 { "cpu initial reset", UVC_CMD_CPU_RESET_INITIAL, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_CPU_RESET_INITIAL },
144 { "prepare clear reset", UVC_CMD_PREPARE_RESET, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_PREPARE_RESET },
145 { "cpu clear reset", UVC_CMD_CPU_RESET_CLEAR, sizeof(struct uv_cb_nodata), BIT_UVC_CMD_CPU_PERFORM_CLEAR_RESET },
146 { "cpu set state", UVC_CMD_CPU_SET_STATE, sizeof(struct uv_cb_cpu_set_state), BIT_UVC_CMD_CPU_SET_STATE },
147 { "pin shared", UVC_CMD_PIN_PAGE_SHARED, sizeof(struct uv_cb_cfs), BIT_UVC_CMD_PIN_PAGE_SHARED },
148 { "unpin shared", UVC_CMD_UNPIN_PAGE_SHARED, sizeof(struct uv_cb_cts), BIT_UVC_CMD_UNPIN_PAGE_SHARED },
149 { NULL, 0, 0 },
150 };
151
test_invalid(void)152 static void test_invalid(void)
153 {
154 struct uv_cb_header *hdr = (void *)page;
155 int cc, i;
156
157 report_prefix_push("invalid");
158 for (i = 0; invalid_cmds[i].name; i++) {
159 hdr->cmd = invalid_cmds[i].cmd;
160 hdr->len = invalid_cmds[i].len;
161 cc = uv_call(0, (u64)hdr);
162 report(cc == 1 && hdr->rc == UVC_RC_INV_CMD &&
163 (invalid_cmds[i].call_bit == -1 || !uv_query_test_call(invalid_cmds[i].call_bit)),
164 "%s", invalid_cmds[i].name);
165 }
166 report_prefix_pop();
167 }
168
test_share_bits(void)169 static void test_share_bits(void)
170 {
171 bool unshare = uv_query_test_call(BIT_UVC_CMD_REMOVE_SHARED_ACCESS);
172 bool share = uv_query_test_call(BIT_UVC_CMD_SET_SHARED_ACCESS);
173
174 report_prefix_push("query");
175 report(!(share ^ unshare), "share bits are identical");
176 report_prefix_pop();
177 }
178
main(void)179 int main(void)
180 {
181 bool has_uvc = test_facility(158);
182
183 report_prefix_push("uvc");
184 if (!has_uvc) {
185 report_skip("Ultravisor call facility is not available");
186 goto done;
187 }
188
189 /*
190 * Needs to be done before the guest-fence,
191 * as the fence tests if both shared bits are present
192 */
193 test_share_bits();
194
195 if (!uv_os_is_guest()) {
196 report_skip("Not a protected guest");
197 goto done;
198 }
199
200 page = (unsigned long)alloc_page();
201 test_priv();
202 test_invalid();
203 test_query();
204 test_sharing();
205 free_page((void *)page);
206 done:
207 report_prefix_pop();
208 return report_summary();
209 }
210