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