1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Snippet definitions 4 * 5 * Copyright IBM Corp. 2021 6 * Author: Janosch Frank <frankja@linux.ibm.com> 7 */ 8 9 #ifndef _S390X_SNIPPET_H_ 10 #define _S390X_SNIPPET_H_ 11 12 #include <sie.h> 13 #include <uv.h> 14 #include <asm/uv.h> 15 16 /* This macro cuts down the length of the pointers to snippets */ 17 #define SNIPPET_NAME_START(type, file) \ 18 _binary_s390x_snippets_##type##_##file##_gbin_start 19 #define SNIPPET_NAME_END(type, file) \ 20 _binary_s390x_snippets_##type##_##file##_gbin_end 21 #define SNIPPET_HDR_START(type, file) \ 22 _binary_s390x_snippets_##type##_##file##_hdr_start 23 #define SNIPPET_HDR_END(type, file) \ 24 _binary_s390x_snippets_##type##_##file##_hdr_end 25 26 27 /* Returns the length of the snippet */ 28 #define SNIPPET_LEN(type, file) \ 29 ((uintptr_t)SNIPPET_NAME_END(type, file) - (uintptr_t)SNIPPET_NAME_START(type, file)) 30 #define SNIPPET_HDR_LEN(type, file) \ 31 ((uintptr_t)SNIPPET_HDR_END(type, file) - (uintptr_t)SNIPPET_HDR_START(type, file)) 32 33 /* 34 * Some of the UV memory needs to be allocated with >31 bit 35 * addresses which means we need a lot more memory than other 36 * tests. 37 */ 38 #define SNIPPET_PV_MIN_MEM_SIZE (SZ_1M * 2200UL) 39 40 #define SNIPPET_PV_TWEAK0 0x42UL 41 #define SNIPPET_PV_TWEAK1 0UL 42 #define SNIPPET_UNPACK_OFF 0 43 44 45 /* 46 * C snippet instructions start at 0x4000 due to the prefix and the 47 * stack being before that. ASM snippets don't strictly need a stack 48 * but keeping the starting address the same means less code. 49 */ 50 #define SNIPPET_ENTRY_ADDR 0x4000 51 52 /* Standard entry PSWs for snippets which can simply be copied into the guest PSW */ 53 static const struct psw snippet_psw = { 54 .mask = PSW_MASK_64, 55 .addr = SNIPPET_ENTRY_ADDR, 56 }; 57 58 /* 59 * Sets up a snippet guest on top of an existing and initialized SIE 60 * vm struct. 61 * Once this function has finished without errors the guest can be started. 62 * 63 * @vm: VM that this function will populated, has to be initialized already 64 * @gbin: Snippet gbin data pointer 65 * @gbin_len: Length of the gbin data 66 * @off: Offset from guest absolute 0x0 where snippet is copied to 67 */ 68 static inline void snippet_init(struct vm *vm, const char *gbin, 69 uint64_t gbin_len, uint64_t off) 70 { 71 uint64_t mso = vm->sblk->mso; 72 73 /* Copy test image to guest memory */ 74 memcpy((void *)mso + off, gbin, gbin_len); 75 76 /* Setup guest PSW */ 77 vm->sblk->gpsw = snippet_psw; 78 79 /* 80 * We want to exit on PGM exceptions so we don't need 81 * exception handlers in the guest. 82 */ 83 vm->sblk->ictl = ICTL_OPEREXC | ICTL_PINT; 84 } 85 86 /* 87 * Sets up a snippet UV/PV guest on top of an existing and initialized 88 * SIE vm struct. 89 * Once this function has finished without errors the guest can be started. 90 * 91 * @vm: VM that this function will populated, has to be initialized already 92 * @gbin: Snippet gbin data pointer 93 * @hdr: Snippet SE header data pointer 94 * @gbin_len: Length of the gbin data 95 * @hdr_len: Length of the hdr data 96 * @off: Offset from guest absolute 0x0 where snippet is copied to 97 */ 98 static inline void snippet_pv_init(struct vm *vm, const char *gbin, 99 const char *hdr, uint64_t gbin_len, 100 uint64_t hdr_len, uint64_t off) 101 { 102 uint64_t tweak[2] = {SNIPPET_PV_TWEAK0, SNIPPET_PV_TWEAK1}; 103 uint64_t mso = vm->sblk->mso; 104 int i; 105 106 snippet_init(vm, gbin, gbin_len, off); 107 108 uv_create_guest(vm); 109 uv_set_se_hdr(vm->uv.vm_handle, (void *)hdr, hdr_len); 110 111 /* Unpack works on guest addresses so we only need off */ 112 uv_unpack(vm, off, gbin_len, tweak[0]); 113 uv_verify_load(vm); 114 115 /* 116 * Manually import: 117 * - lowcore 0x0 - 0x1000 (asm) 118 * - stack 0x3000 (C) 119 */ 120 for (i = 0; i < 4; i++) { 121 uv_import(vm->uv.vm_handle, mso + PAGE_SIZE * i); 122 } 123 } 124 125 /* Allocates and sets up a snippet based guest */ 126 static inline void snippet_setup_guest(struct vm *vm, bool is_pv) 127 { 128 const unsigned long guest_size = SZ_1M; 129 uint8_t *guest_start = sie_guest_alloc(guest_size); 130 131 /* Initialize the vm struct and allocate control blocks */ 132 sie_guest_create(vm, (uint64_t)guest_start, guest_size); 133 134 if (is_pv) { 135 /* FMT4 needs a ESCA */ 136 sie_guest_sca_create(vm); 137 138 /* 139 * Initialize UV and setup the address spaces needed 140 * to run a PV guest. 141 */ 142 uv_init(); 143 uv_setup_asces(); 144 } 145 } 146 147 #endif 148