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