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> 21*ec6683adSJanosch 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 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 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 42*ec6683adSJanosch Frank bool uv_host_requirement_checks(void) 43*ec6683adSJanosch Frank { 44*ec6683adSJanosch Frank if (!test_facility(158)) { 45*ec6683adSJanosch Frank report_skip("UV Call facility unavailable"); 46*ec6683adSJanosch Frank return false; 47*ec6683adSJanosch Frank } 48*ec6683adSJanosch Frank if (!sclp_facilities.has_sief2) { 49*ec6683adSJanosch Frank report_skip("SIEF2 facility unavailable"); 50*ec6683adSJanosch Frank return false; 51*ec6683adSJanosch Frank } 52*ec6683adSJanosch Frank if (get_ram_size() < SNIPPET_PV_MIN_MEM_SIZE) { 53*ec6683adSJanosch Frank report_skip("Not enough memory. This test needs about %ld MB of memory", 54*ec6683adSJanosch Frank SNIPPET_PV_MIN_MEM_SIZE / SZ_1M); 55*ec6683adSJanosch Frank return false; 56*ec6683adSJanosch Frank } 57*ec6683adSJanosch Frank 58*ec6683adSJanosch Frank return true; 59*ec6683adSJanosch Frank } 60*ec6683adSJanosch Frank 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 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 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 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 */ 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 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; 149f4f97af0SJanosch Frank uvcb_cgc.guest_stor_len = vm->sblk->msl; 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 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 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 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