b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* |
| 3 | * Ultravisor Interfaces |
| 4 | * |
| 5 | * Copyright IBM Corp. 2019 |
| 6 | * |
| 7 | * Author(s): |
| 8 | * Vasily Gorbik <gor@linux.ibm.com> |
| 9 | * Janosch Frank <frankja@linux.ibm.com> |
| 10 | */ |
| 11 | #ifndef _ASM_S390_UV_H |
| 12 | #define _ASM_S390_UV_H |
| 13 | |
| 14 | #include <linux/types.h> |
| 15 | #include <linux/errno.h> |
| 16 | #include <linux/bug.h> |
| 17 | #include <asm/page.h> |
| 18 | |
| 19 | #define UVC_RC_EXECUTED 0x0001 |
| 20 | #define UVC_RC_INV_CMD 0x0002 |
| 21 | #define UVC_RC_INV_STATE 0x0003 |
| 22 | #define UVC_RC_INV_LEN 0x0005 |
| 23 | #define UVC_RC_NO_RESUME 0x0007 |
| 24 | |
| 25 | #define UVC_CMD_QUI 0x0001 |
| 26 | #define UVC_CMD_SET_SHARED_ACCESS 0x1000 |
| 27 | #define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001 |
| 28 | |
| 29 | /* Bits in installed uv calls */ |
| 30 | enum uv_cmds_inst { |
| 31 | BIT_UVC_CMD_QUI = 0, |
| 32 | BIT_UVC_CMD_SET_SHARED_ACCESS = 8, |
| 33 | BIT_UVC_CMD_REMOVE_SHARED_ACCESS = 9, |
| 34 | }; |
| 35 | |
| 36 | struct uv_cb_header { |
| 37 | u16 len; |
| 38 | u16 cmd; /* Command Code */ |
| 39 | u16 rc; /* Response Code */ |
| 40 | u16 rrc; /* Return Reason Code */ |
| 41 | } __packed __aligned(8); |
| 42 | |
| 43 | struct uv_cb_qui { |
| 44 | struct uv_cb_header header; |
| 45 | u64 reserved08; |
| 46 | u64 inst_calls_list[4]; |
| 47 | u64 reserved30[15]; |
| 48 | } __packed __aligned(8); |
| 49 | |
| 50 | struct uv_cb_share { |
| 51 | struct uv_cb_header header; |
| 52 | u64 reserved08[3]; |
| 53 | u64 paddr; |
| 54 | u64 reserved28; |
| 55 | } __packed __aligned(8); |
| 56 | |
| 57 | static inline int uv_call(unsigned long r1, unsigned long r2) |
| 58 | { |
| 59 | int cc; |
| 60 | |
| 61 | asm volatile( |
| 62 | "0: .insn rrf,0xB9A40000,%[r1],%[r2],0,0\n" |
| 63 | " brc 3,0b\n" |
| 64 | " ipm %[cc]\n" |
| 65 | " srl %[cc],28\n" |
| 66 | : [cc] "=d" (cc) |
| 67 | : [r1] "a" (r1), [r2] "a" (r2) |
| 68 | : "memory", "cc"); |
| 69 | return cc; |
| 70 | } |
| 71 | |
| 72 | #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST |
| 73 | extern int prot_virt_guest; |
| 74 | |
| 75 | static inline int is_prot_virt_guest(void) |
| 76 | { |
| 77 | return prot_virt_guest; |
| 78 | } |
| 79 | |
| 80 | static inline int share(unsigned long addr, u16 cmd) |
| 81 | { |
| 82 | struct uv_cb_share uvcb = { |
| 83 | .header.cmd = cmd, |
| 84 | .header.len = sizeof(uvcb), |
| 85 | .paddr = addr |
| 86 | }; |
| 87 | |
| 88 | if (!is_prot_virt_guest()) |
| 89 | return -ENOTSUPP; |
| 90 | /* |
| 91 | * Sharing is page wise, if we encounter addresses that are |
| 92 | * not page aligned, we assume something went wrong. If |
| 93 | * malloced structs are passed to this function, we could leak |
| 94 | * data to the hypervisor. |
| 95 | */ |
| 96 | BUG_ON(addr & ~PAGE_MASK); |
| 97 | |
| 98 | if (!uv_call(0, (u64)&uvcb)) |
| 99 | return 0; |
| 100 | pr_err("%s UVC failed (rc: 0x%x, rrc: 0x%x), possible hypervisor bug.\n", |
| 101 | uvcb.header.cmd == UVC_CMD_SET_SHARED_ACCESS ? "Share" : "Unshare", |
| 102 | uvcb.header.rc, uvcb.header.rrc); |
| 103 | panic("System security cannot be guaranteed unless the system panics now.\n"); |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | * Guest 2 request to the Ultravisor to make a page shared with the |
| 108 | * hypervisor for IO. |
| 109 | * |
| 110 | * @addr: Real or absolute address of the page to be shared |
| 111 | */ |
| 112 | static inline int uv_set_shared(unsigned long addr) |
| 113 | { |
| 114 | return share(addr, UVC_CMD_SET_SHARED_ACCESS); |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | * Guest 2 request to the Ultravisor to make a page unshared. |
| 119 | * |
| 120 | * @addr: Real or absolute address of the page to be unshared |
| 121 | */ |
| 122 | static inline int uv_remove_shared(unsigned long addr) |
| 123 | { |
| 124 | return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS); |
| 125 | } |
| 126 | |
| 127 | void uv_query_info(void); |
| 128 | #else |
| 129 | #define is_prot_virt_guest() 0 |
| 130 | static inline int uv_set_shared(unsigned long addr) { return 0; } |
| 131 | static inline int uv_remove_shared(unsigned long addr) { return 0; } |
| 132 | static inline void uv_query_info(void) {} |
| 133 | #endif |
| 134 | |
| 135 | #endif /* _ASM_S390_UV_H */ |