1/*	$NetBSD: page_track.c,v 1.2 2021/12/18 23:45:31 riastradh Exp $	*/
2
3/*
4 * Copyright(c) 2011-2017 Intel Corporation. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25#include <sys/cdefs.h>
26__KERNEL_RCSID(0, "$NetBSD: page_track.c,v 1.2 2021/12/18 23:45:31 riastradh Exp $");
27
28#include "i915_drv.h"
29#include "gvt.h"
30
31/**
32 * intel_vgpu_find_page_track - find page track rcord of guest page
33 * @vgpu: a vGPU
34 * @gfn: the gfn of guest page
35 *
36 * Returns:
37 * A pointer to struct intel_vgpu_page_track if found, else NULL returned.
38 */
39struct intel_vgpu_page_track *intel_vgpu_find_page_track(
40		struct intel_vgpu *vgpu, unsigned long gfn)
41{
42	return radix_tree_lookup(&vgpu->page_track_tree, gfn);
43}
44
45/**
46 * intel_vgpu_register_page_track - register a guest page to be tacked
47 * @vgpu: a vGPU
48 * @gfn: the gfn of guest page
49 * @handler: page track handler
50 * @priv: tracker private
51 *
52 * Returns:
53 * zero on success, negative error code if failed.
54 */
55int intel_vgpu_register_page_track(struct intel_vgpu *vgpu, unsigned long gfn,
56		gvt_page_track_handler_t handler, void *priv)
57{
58	struct intel_vgpu_page_track *track;
59	int ret;
60
61	track = intel_vgpu_find_page_track(vgpu, gfn);
62	if (track)
63		return -EEXIST;
64
65	track = kzalloc(sizeof(*track), GFP_KERNEL);
66	if (!track)
67		return -ENOMEM;
68
69	track->handler = handler;
70	track->priv_data = priv;
71
72	ret = radix_tree_insert(&vgpu->page_track_tree, gfn, track);
73	if (ret) {
74		kfree(track);
75		return ret;
76	}
77
78	return 0;
79}
80
81/**
82 * intel_vgpu_unregister_page_track - unregister the tracked guest page
83 * @vgpu: a vGPU
84 * @gfn: the gfn of guest page
85 *
86 */
87void intel_vgpu_unregister_page_track(struct intel_vgpu *vgpu,
88		unsigned long gfn)
89{
90	struct intel_vgpu_page_track *track;
91
92	track = radix_tree_delete(&vgpu->page_track_tree, gfn);
93	if (track) {
94		if (track->tracked)
95			intel_gvt_hypervisor_disable_page_track(vgpu, gfn);
96		kfree(track);
97	}
98}
99
100/**
101 * intel_vgpu_enable_page_track - set write-protection on guest page
102 * @vgpu: a vGPU
103 * @gfn: the gfn of guest page
104 *
105 * Returns:
106 * zero on success, negative error code if failed.
107 */
108int intel_vgpu_enable_page_track(struct intel_vgpu *vgpu, unsigned long gfn)
109{
110	struct intel_vgpu_page_track *track;
111	int ret;
112
113	track = intel_vgpu_find_page_track(vgpu, gfn);
114	if (!track)
115		return -ENXIO;
116
117	if (track->tracked)
118		return 0;
119
120	ret = intel_gvt_hypervisor_enable_page_track(vgpu, gfn);
121	if (ret)
122		return ret;
123	track->tracked = true;
124	return 0;
125}
126
127/**
128 * intel_vgpu_enable_page_track - cancel write-protection on guest page
129 * @vgpu: a vGPU
130 * @gfn: the gfn of guest page
131 *
132 * Returns:
133 * zero on success, negative error code if failed.
134 */
135int intel_vgpu_disable_page_track(struct intel_vgpu *vgpu, unsigned long gfn)
136{
137	struct intel_vgpu_page_track *track;
138	int ret;
139
140	track = intel_vgpu_find_page_track(vgpu, gfn);
141	if (!track)
142		return -ENXIO;
143
144	if (!track->tracked)
145		return 0;
146
147	ret = intel_gvt_hypervisor_disable_page_track(vgpu, gfn);
148	if (ret)
149		return ret;
150	track->tracked = false;
151	return 0;
152}
153
154/**
155 * intel_vgpu_page_track_handler - called when write to write-protected page
156 * @vgpu: a vGPU
157 * @gpa: the gpa of this write
158 * @data: the writed data
159 * @bytes: the length of this write
160 *
161 * Returns:
162 * zero on success, negative error code if failed.
163 */
164int intel_vgpu_page_track_handler(struct intel_vgpu *vgpu, u64 gpa,
165		void *data, unsigned int bytes)
166{
167	struct intel_vgpu_page_track *page_track;
168	int ret = 0;
169
170	mutex_lock(&vgpu->vgpu_lock);
171
172	page_track = intel_vgpu_find_page_track(vgpu, gpa >> PAGE_SHIFT);
173	if (!page_track) {
174		ret = -ENXIO;
175		goto out;
176	}
177
178	if (unlikely(vgpu->failsafe)) {
179		/* Remove write protection to prevent furture traps. */
180		intel_vgpu_disable_page_track(vgpu, gpa >> PAGE_SHIFT);
181	} else {
182		ret = page_track->handler(page_track, gpa, data, bytes);
183		if (ret)
184			gvt_err("guest page write error, gpa %llx\n", gpa);
185	}
186
187out:
188	mutex_unlock(&vgpu->vgpu_lock);
189	return ret;
190}
191