1aa8794b1SJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */
2aa8794b1SJanosch Frank /*
3aa8794b1SJanosch Frank * Ultravisor related functionality
4aa8794b1SJanosch Frank *
5aa8794b1SJanosch Frank * Copyright 2020 IBM Corp.
6aa8794b1SJanosch Frank *
7aa8794b1SJanosch Frank * Authors:
8aa8794b1SJanosch Frank * Janosch Frank <frankja@linux.ibm.com>
9aa8794b1SJanosch Frank */
1007ac63dcSJanosch Frank #include <libcflat.h>
1107ac63dcSJanosch Frank #include <bitops.h>
1207ac63dcSJanosch Frank #include <alloc.h>
1307ac63dcSJanosch Frank #include <alloc_page.h>
1407ac63dcSJanosch Frank #include <asm/page.h>
1507ac63dcSJanosch Frank #include <asm/arch_def.h>
1607ac63dcSJanosch Frank
1707ac63dcSJanosch Frank #include <asm/facility.h>
1807ac63dcSJanosch Frank #include <asm/uv.h>
1907ac63dcSJanosch Frank #include <uv.h>
20f4f97af0SJanosch Frank #include <sie.h>
21ec6683adSJanosch Frank #include <snippet.h>
2207ac63dcSJanosch Frank
2307ac63dcSJanosch Frank static struct uv_cb_qui uvcb_qui = {
2407ac63dcSJanosch Frank .header.cmd = UVC_CMD_QUI,
2507ac63dcSJanosch Frank .header.len = sizeof(uvcb_qui),
2607ac63dcSJanosch Frank };
27f4f97af0SJanosch Frank static uint64_t uv_init_mem;
28f4f97af0SJanosch Frank
2907ac63dcSJanosch Frank
uv_os_is_guest(void)3007ac63dcSJanosch Frank bool uv_os_is_guest(void)
3107ac63dcSJanosch Frank {
3207ac63dcSJanosch Frank return test_facility(158) &&
3307ac63dcSJanosch Frank uv_query_test_call(BIT_UVC_CMD_SET_SHARED_ACCESS) &&
3407ac63dcSJanosch Frank uv_query_test_call(BIT_UVC_CMD_REMOVE_SHARED_ACCESS);
3507ac63dcSJanosch Frank }
3607ac63dcSJanosch Frank
uv_os_is_host(void)3707ac63dcSJanosch Frank bool uv_os_is_host(void)
3807ac63dcSJanosch Frank {
3907ac63dcSJanosch Frank return test_facility(158) && uv_query_test_call(BIT_UVC_CMD_INIT_UV);
4007ac63dcSJanosch Frank }
4107ac63dcSJanosch Frank
uv_host_requirement_checks(void)42ec6683adSJanosch Frank bool uv_host_requirement_checks(void)
43ec6683adSJanosch Frank {
44ec6683adSJanosch Frank if (!test_facility(158)) {
45ec6683adSJanosch Frank report_skip("UV Call facility unavailable");
46ec6683adSJanosch Frank return false;
47ec6683adSJanosch Frank }
48ec6683adSJanosch Frank if (!sclp_facilities.has_sief2) {
49ec6683adSJanosch Frank report_skip("SIEF2 facility unavailable");
50ec6683adSJanosch Frank return false;
51ec6683adSJanosch Frank }
52ec6683adSJanosch Frank if (get_ram_size() < SNIPPET_PV_MIN_MEM_SIZE) {
53ec6683adSJanosch Frank report_skip("Not enough memory. This test needs about %ld MB of memory",
54ec6683adSJanosch Frank SNIPPET_PV_MIN_MEM_SIZE / SZ_1M);
55ec6683adSJanosch Frank return false;
56ec6683adSJanosch Frank }
57ec6683adSJanosch Frank
58ec6683adSJanosch Frank return true;
59ec6683adSJanosch Frank }
60ec6683adSJanosch Frank
uv_query_test_call(unsigned int nr)6107ac63dcSJanosch Frank bool uv_query_test_call(unsigned int nr)
6207ac63dcSJanosch Frank {
6307ac63dcSJanosch Frank /* Query needs to be called first */
6407ac63dcSJanosch Frank assert(uvcb_qui.header.rc);
6507ac63dcSJanosch Frank assert(nr < BITS_PER_LONG * ARRAY_SIZE(uvcb_qui.inst_calls_list));
6607ac63dcSJanosch Frank
6707ac63dcSJanosch Frank return test_bit_inv(nr, uvcb_qui.inst_calls_list);
6807ac63dcSJanosch Frank }
6907ac63dcSJanosch Frank
uv_get_query_data(void)7026184212SSteffen Eiden const struct uv_cb_qui *uv_get_query_data(void)
7126184212SSteffen Eiden {
7226184212SSteffen Eiden /* Query needs to be called first */
7326184212SSteffen Eiden assert(uvcb_qui.header.rc == 1 || uvcb_qui.header.rc == 0x100);
7426184212SSteffen Eiden
7526184212SSteffen Eiden return &uvcb_qui;
7626184212SSteffen Eiden }
7726184212SSteffen Eiden
uv_setup(void)7807ac63dcSJanosch Frank int uv_setup(void)
7907ac63dcSJanosch Frank {
8007ac63dcSJanosch Frank if (!test_facility(158))
8107ac63dcSJanosch Frank return 0;
8207ac63dcSJanosch Frank
8378168306SJanosch Frank uv_call(0, (u64)&uvcb_qui);
8478168306SJanosch Frank
8578168306SJanosch Frank assert(uvcb_qui.header.rc == 1 || uvcb_qui.header.rc == 0x100);
8607ac63dcSJanosch Frank return 1;
8707ac63dcSJanosch Frank }
88f4f97af0SJanosch Frank
uv_init(void)89f4f97af0SJanosch Frank void uv_init(void)
90f4f97af0SJanosch Frank {
91f4f97af0SJanosch Frank struct uv_cb_init uvcb_init = {
92f4f97af0SJanosch Frank .header.len = sizeof(uvcb_init),
93f4f97af0SJanosch Frank .header.cmd = UVC_CMD_INIT_UV,
94f4f97af0SJanosch Frank };
95f4f97af0SJanosch Frank static bool initialized;
96f4f97af0SJanosch Frank int cc;
97f4f97af0SJanosch Frank
98f4f97af0SJanosch Frank /* Let's not do this twice */
99d558b81cSJanosch Frank if (initialized)
100d558b81cSJanosch Frank return;
101f4f97af0SJanosch Frank /* Query is done on initialization but let's check anyway */
102f4f97af0SJanosch Frank assert(uvcb_qui.header.rc == 1 || uvcb_qui.header.rc == 0x100);
103f4f97af0SJanosch Frank
104f4f97af0SJanosch Frank /* Donated storage needs to be over 2GB aligned to 1MB */
105f4f97af0SJanosch Frank uv_init_mem = (uint64_t)memalign_pages_flags(HPAGE_SIZE, uvcb_qui.uv_base_stor_len, AREA_NORMAL);
106f4f97af0SJanosch Frank uvcb_init.stor_origin = uv_init_mem;
107f4f97af0SJanosch Frank uvcb_init.stor_len = uvcb_qui.uv_base_stor_len;
108f4f97af0SJanosch Frank
109f4f97af0SJanosch Frank cc = uv_call(0, (uint64_t)&uvcb_init);
110f4f97af0SJanosch Frank assert(cc == 0);
111f4f97af0SJanosch Frank initialized = true;
112f4f97af0SJanosch Frank }
113f4f97af0SJanosch Frank
1149e3cff66SJanosch Frank /*
1159e3cff66SJanosch Frank * Create a new ASCE for the UV config because they can't be shared
1169e3cff66SJanosch Frank * for security reasons. We just simply copy the top most table into a
1179e3cff66SJanosch Frank * fresh set of allocated pages and use those pages as the asce.
1189e3cff66SJanosch Frank */
create_asce(void)1199e3cff66SJanosch Frank static uint64_t create_asce(void)
1209e3cff66SJanosch Frank {
1219e3cff66SJanosch Frank void *pgd_new, *pgd_old;
1229e3cff66SJanosch Frank uint64_t asce = stctg(1);
1239e3cff66SJanosch Frank
1249e3cff66SJanosch Frank pgd_new = memalign_pages(PAGE_SIZE, PAGE_SIZE * 4);
1259e3cff66SJanosch Frank pgd_old = (void *)(asce & PAGE_MASK);
1269e3cff66SJanosch Frank
1279e3cff66SJanosch Frank memcpy(pgd_new, pgd_old, PAGE_SIZE * 4);
1289e3cff66SJanosch Frank
1299e3cff66SJanosch Frank asce = __pa(pgd_new) | ASCE_P | (asce & (ASCE_DT | ASCE_TL));
1309e3cff66SJanosch Frank return asce;
1319e3cff66SJanosch Frank }
1329e3cff66SJanosch Frank
uv_create_guest(struct vm * vm)133f4f97af0SJanosch Frank void uv_create_guest(struct vm *vm)
134f4f97af0SJanosch Frank {
135f4f97af0SJanosch Frank struct uv_cb_cgc uvcb_cgc = {
136f4f97af0SJanosch Frank .header.cmd = UVC_CMD_CREATE_SEC_CONF,
137f4f97af0SJanosch Frank .header.len = sizeof(uvcb_cgc),
138f4f97af0SJanosch Frank };
139f4f97af0SJanosch Frank struct uv_cb_csc uvcb_csc = {
140f4f97af0SJanosch Frank .header.len = sizeof(uvcb_csc),
141f4f97af0SJanosch Frank .header.cmd = UVC_CMD_CREATE_SEC_CPU,
142f4f97af0SJanosch Frank .state_origin = (uint64_t)vm->sblk,
143f4f97af0SJanosch Frank .num = 0,
144f4f97af0SJanosch Frank };
145f4f97af0SJanosch Frank unsigned long vsize;
146f4f97af0SJanosch Frank int cc;
147f4f97af0SJanosch Frank
148f4f97af0SJanosch Frank uvcb_cgc.guest_stor_origin = vm->sblk->mso;
149*f08ec4e5SClaudio Imbrenda uvcb_cgc.guest_stor_len = vm->sblk->msl - vm->sblk->mso + SZ_1M;
150f4f97af0SJanosch Frank
151f4f97af0SJanosch Frank /* Config allocation */
152f4f97af0SJanosch Frank vsize = uvcb_qui.conf_base_virt_stor_len +
153f4f97af0SJanosch Frank ((uvcb_cgc.guest_stor_len / HPAGE_SIZE) * uvcb_qui.conf_virt_var_stor_len);
154f4f97af0SJanosch Frank
155f4f97af0SJanosch Frank vm->uv.conf_base_stor = memalign_pages_flags(PAGE_SIZE * 4, uvcb_qui.conf_base_phys_stor_len, 0);
156f4f97af0SJanosch Frank /*
157f4f97af0SJanosch Frank * This allocation needs to be below the max guest storage
158f4f97af0SJanosch Frank * address so let's simply put it into the physical memory
159f4f97af0SJanosch Frank */
160f4f97af0SJanosch Frank vm->uv.conf_var_stor = memalign_pages_flags(PAGE_SIZE, vsize,0);
161f4f97af0SJanosch Frank uvcb_cgc.conf_base_stor_origin = (uint64_t)vm->uv.conf_base_stor;
162f4f97af0SJanosch Frank uvcb_cgc.conf_var_stor_origin = (uint64_t)vm->uv.conf_var_stor;
163f4f97af0SJanosch Frank
164f4f97af0SJanosch Frank /* CPU allocation */
165f4f97af0SJanosch Frank vm->uv.cpu_stor = memalign_pages_flags(PAGE_SIZE, uvcb_qui.cpu_stor_len, 0);
166f4f97af0SJanosch Frank uvcb_csc.stor_origin = (uint64_t)vm->uv.cpu_stor;
167f4f97af0SJanosch Frank
1689e3cff66SJanosch Frank uvcb_cgc.guest_asce = create_asce();
1699e3cff66SJanosch Frank vm->save_area.guest.asce = uvcb_cgc.guest_asce;
170f4f97af0SJanosch Frank uvcb_cgc.guest_sca = (uint64_t)vm->sca;
171f4f97af0SJanosch Frank
172f4f97af0SJanosch Frank cc = uv_call(0, (uint64_t)&uvcb_cgc);
173f4f97af0SJanosch Frank assert(!cc);
174f4f97af0SJanosch Frank
175f4f97af0SJanosch Frank vm->uv.vm_handle = uvcb_cgc.guest_handle;
176f4f97af0SJanosch Frank uvcb_csc.guest_handle = uvcb_cgc.guest_handle;
177f4f97af0SJanosch Frank cc = uv_call(0, (uint64_t)&uvcb_csc);
178f4f97af0SJanosch Frank vm->uv.vcpu_handle = uvcb_csc.cpu_handle;
179f4f97af0SJanosch Frank assert(!cc);
180f4f97af0SJanosch Frank
181f4f97af0SJanosch Frank /*
182f4f97af0SJanosch Frank * Convert guest to format 4:
183f4f97af0SJanosch Frank *
184f4f97af0SJanosch Frank * - Set format 4
185f4f97af0SJanosch Frank * - Write UV handles into sblk
186f4f97af0SJanosch Frank * - Allocate and set SIDA
187f4f97af0SJanosch Frank */
188f4f97af0SJanosch Frank vm->sblk->sdf = 2;
189f4f97af0SJanosch Frank vm->sblk->sidad = (uint64_t)alloc_page();
190f4f97af0SJanosch Frank vm->sblk->pv_handle_cpu = uvcb_csc.cpu_handle;
191f4f97af0SJanosch Frank vm->sblk->pv_handle_config = uvcb_cgc.guest_handle;
192f4f97af0SJanosch Frank }
193f4f97af0SJanosch Frank
uv_destroy_guest(struct vm * vm)194f4f97af0SJanosch Frank void uv_destroy_guest(struct vm *vm)
195f4f97af0SJanosch Frank {
196f4f97af0SJanosch Frank int cc;
197f4f97af0SJanosch Frank u16 rc, rrc;
198f4f97af0SJanosch Frank
199f4f97af0SJanosch Frank cc = uv_cmd_nodata(vm->sblk->pv_handle_cpu,
200f4f97af0SJanosch Frank UVC_CMD_DESTROY_SEC_CPU, &rc, &rrc);
201f4f97af0SJanosch Frank assert(cc == 0);
202f4f97af0SJanosch Frank free_page((void *)vm->sblk->sidad);
203f4f97af0SJanosch Frank free_pages(vm->uv.cpu_stor);
204f4f97af0SJanosch Frank
205f4f97af0SJanosch Frank cc = uv_cmd_nodata(vm->sblk->pv_handle_config,
206f4f97af0SJanosch Frank UVC_CMD_DESTROY_SEC_CONF, &rc, &rrc);
207f4f97af0SJanosch Frank assert(cc == 0);
208f4f97af0SJanosch Frank free_pages(vm->uv.conf_base_stor);
209f4f97af0SJanosch Frank free_pages(vm->uv.conf_var_stor);
2109e3cff66SJanosch Frank
2119e3cff66SJanosch Frank free_pages((void *)(vm->uv.asce & PAGE_MASK));
212d558b81cSJanosch Frank memset(&vm->uv, 0, sizeof(vm->uv));
213d558b81cSJanosch Frank
214d558b81cSJanosch Frank /* Convert the sblk back to non-PV */
215d558b81cSJanosch Frank vm->save_area.guest.asce = stctg(1);
216d558b81cSJanosch Frank vm->sblk->sdf = 0;
217d558b81cSJanosch Frank vm->sblk->sidad = 0;
218d558b81cSJanosch Frank vm->sblk->pv_handle_cpu = 0;
219d558b81cSJanosch Frank vm->sblk->pv_handle_config = 0;
220f4f97af0SJanosch Frank }
221f4f97af0SJanosch Frank
uv_unpack(struct vm * vm,uint64_t addr,uint64_t len,uint64_t tweak)222f4f97af0SJanosch Frank int uv_unpack(struct vm *vm, uint64_t addr, uint64_t len, uint64_t tweak)
223f4f97af0SJanosch Frank {
224f4f97af0SJanosch Frank int i, cc;
225f4f97af0SJanosch Frank
226f4f97af0SJanosch Frank for (i = 0; i < len / PAGE_SIZE; i++) {
227f4f97af0SJanosch Frank cc = uv_unp_page(vm->uv.vm_handle, addr, tweak, i * PAGE_SIZE);
228f4f97af0SJanosch Frank assert(!cc);
229f4f97af0SJanosch Frank addr += PAGE_SIZE;
230f4f97af0SJanosch Frank }
231f4f97af0SJanosch Frank return cc;
232f4f97af0SJanosch Frank }
233f4f97af0SJanosch Frank
uv_verify_load(struct vm * vm)234f4f97af0SJanosch Frank void uv_verify_load(struct vm *vm)
235f4f97af0SJanosch Frank {
236f4f97af0SJanosch Frank uint16_t rc, rrc;
237f4f97af0SJanosch Frank int cc;
238f4f97af0SJanosch Frank
239f4f97af0SJanosch Frank cc = uv_cmd_nodata(vm->uv.vm_handle, UVC_CMD_VERIFY_IMG, &rc, &rrc);
240f4f97af0SJanosch Frank assert(!cc);
241f4f97af0SJanosch Frank cc = uv_set_cpu_state(vm->uv.vcpu_handle, PV_CPU_STATE_OPR_LOAD);
242f4f97af0SJanosch Frank assert(!cc);
243f4f97af0SJanosch Frank }
244