1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright 2020-21 IBM Corp.
6 #define pr_fmt(fmt) "vas: " fmt
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/export.h>
11 #include <linux/types.h>
12 #include <linux/delay.h>
13 #include <asm/machdep.h>
14 #include <asm/hvcall.h>
15 #include <asm/plpar_wrappers.h>
19 #define VAS_INVALID_WIN_ADDRESS 0xFFFFFFFFFFFFFFFFul
20 #define VAS_DEFAULT_DOMAIN_ID 0xFFFFFFFFFFFFFFFFul
21 /* The hypervisor allows one credit per window right now */
22 #define DEF_WIN_CREDS 1
24 static struct vas_all_caps caps_all;
25 static bool copypaste_feat;
27 static struct vas_caps vascaps[VAS_MAX_FEAT_TYPE];
29 static long hcall_return_busy_check(long rc)
31 /* Check if we are stalled for some time */
32 if (H_IS_LONG_BUSY(rc)) {
33 msleep(get_longbusy_msecs(rc));
35 } else if (rc == H_BUSY) {
43 * Allocate VAS window hcall
45 static int h_allocate_vas_window(struct pseries_vas_window *win, u64 *domain,
46 u8 wintype, u16 credits)
48 long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
52 rc = plpar_hcall9(H_ALLOCATE_VAS_WINDOW, retbuf, wintype,
53 credits, domain[0], domain[1], domain[2],
54 domain[3], domain[4], domain[5]);
56 rc = hcall_return_busy_check(rc);
57 } while (rc == H_BUSY);
59 if (rc == H_SUCCESS) {
60 if (win->win_addr == VAS_INVALID_WIN_ADDRESS) {
61 pr_err("H_ALLOCATE_VAS_WINDOW: COPY/PASTE is not supported\n");
64 win->vas_win.winid = retbuf[0];
65 win->win_addr = retbuf[1];
66 win->complete_irq = retbuf[2];
67 win->fault_irq = retbuf[3];
71 pr_err("H_ALLOCATE_VAS_WINDOW error: %ld, wintype: %u, credits: %u\n",
72 rc, wintype, credits);
78 * Deallocate VAS window hcall.
80 static int h_deallocate_vas_window(u64 winid)
85 rc = plpar_hcall_norets(H_DEALLOCATE_VAS_WINDOW, winid);
87 rc = hcall_return_busy_check(rc);
88 } while (rc == H_BUSY);
93 pr_err("H_DEALLOCATE_VAS_WINDOW error: %ld, winid: %llu\n",
100 * After the window is opened with allocate window hcall, configure it
101 * with flags and LPAR PID before using.
103 static int h_modify_vas_window(struct pseries_vas_window *win)
106 u32 lpid = mfspr(SPRN_PID);
109 * AMR value is not supported in Linux VAS implementation.
110 * The hypervisor ignores it if 0 is passed.
113 rc = plpar_hcall_norets(H_MODIFY_VAS_WINDOW,
114 win->vas_win.winid, lpid, 0,
115 VAS_MOD_WIN_FLAGS, 0);
117 rc = hcall_return_busy_check(rc);
118 } while (rc == H_BUSY);
123 pr_err("H_MODIFY_VAS_WINDOW error: %ld, winid %u lpid %u\n",
124 rc, win->vas_win.winid, lpid);
129 * This hcall is used to determine the capabilities from the hypervisor.
130 * @hcall: H_QUERY_VAS_CAPABILITIES or H_QUERY_NX_CAPABILITIES
131 * @query_type: If 0 is passed, the hypervisor returns the overall
132 * capabilities which provides all feature(s) that are
133 * available. Then query the hypervisor to get the
134 * corresponding capabilities for the specific feature.
135 * Example: H_QUERY_VAS_CAPABILITIES provides VAS GZIP QoS
136 * and VAS GZIP Default capabilities.
137 * H_QUERY_NX_CAPABILITIES provides NX GZIP
139 * @result: Return buffer to save capabilities.
141 int h_query_vas_capabilities(const u64 hcall, u8 query_type, u64 result)
145 rc = plpar_hcall_norets(hcall, query_type, result);
150 pr_err("HCALL(%llx) error %ld, query_type %u, result buffer 0x%llx\n",
151 hcall, rc, query_type, result);
156 * Get the specific capabilities based on the feature type.
157 * Right now supports GZIP default and GZIP QoS capabilities.
159 static int get_vas_capabilities(u8 feat, enum vas_cop_feat_type type,
160 struct hv_vas_cop_feat_caps *hv_caps)
162 struct vas_cop_feat_caps *caps;
163 struct vas_caps *vcaps;
166 vcaps = &vascaps[type];
167 memset(vcaps, 0, sizeof(*vcaps));
168 INIT_LIST_HEAD(&vcaps->list);
172 rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, feat,
173 (u64)virt_to_phys(hv_caps));
177 caps->user_mode = hv_caps->user_mode;
178 if (!(caps->user_mode & VAS_COPY_PASTE_USER_MODE)) {
179 pr_err("User space COPY/PASTE is not supported\n");
183 caps->descriptor = be64_to_cpu(hv_caps->descriptor);
184 caps->win_type = hv_caps->win_type;
185 if (caps->win_type >= VAS_MAX_FEAT_TYPE) {
186 pr_err("Unsupported window type %u\n", caps->win_type);
189 caps->max_lpar_creds = be16_to_cpu(hv_caps->max_lpar_creds);
190 caps->max_win_creds = be16_to_cpu(hv_caps->max_win_creds);
191 atomic_set(&caps->target_lpar_creds,
192 be16_to_cpu(hv_caps->target_lpar_creds));
193 if (feat == VAS_GZIP_DEF_FEAT) {
194 caps->def_lpar_creds = be16_to_cpu(hv_caps->def_lpar_creds);
196 if (caps->max_win_creds < DEF_WIN_CREDS) {
197 pr_err("Window creds(%u) > max allowed window creds(%u)\n",
198 DEF_WIN_CREDS, caps->max_win_creds);
203 copypaste_feat = true;
208 static int __init pseries_vas_init(void)
210 struct hv_vas_cop_feat_caps *hv_cop_caps;
211 struct hv_vas_all_caps *hv_caps;
215 * Linux supports user space COPY/PASTE only with Radix
217 if (!radix_enabled()) {
218 pr_err("API is supported only with radix page tables\n");
222 hv_caps = kmalloc(sizeof(*hv_caps), GFP_KERNEL);
226 * Get VAS overall capabilities by passing 0 to feature type.
228 rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, 0,
229 (u64)virt_to_phys(hv_caps));
233 caps_all.descriptor = be64_to_cpu(hv_caps->descriptor);
234 caps_all.feat_type = be64_to_cpu(hv_caps->feat_type);
236 hv_cop_caps = kmalloc(sizeof(*hv_cop_caps), GFP_KERNEL);
242 * QOS capabilities available
244 if (caps_all.feat_type & VAS_GZIP_QOS_FEAT_BIT) {
245 rc = get_vas_capabilities(VAS_GZIP_QOS_FEAT,
246 VAS_GZIP_QOS_FEAT_TYPE, hv_cop_caps);
252 * Default capabilities available
254 if (caps_all.feat_type & VAS_GZIP_DEF_FEAT_BIT) {
255 rc = get_vas_capabilities(VAS_GZIP_DEF_FEAT,
256 VAS_GZIP_DEF_FEAT_TYPE, hv_cop_caps);
261 pr_info("GZIP feature is available\n");
269 machine_device_initcall(pseries, pseries_vas_init);