1 | /* |
2 | * Copyright(c) 2011-2016 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 | * Authors: |
24 | * Zhi Wang <zhi.a.wang@intel.com> |
25 | * |
26 | * Contributors: |
27 | * Changbin Du <changbin.du@intel.com> |
28 | * |
29 | */ |
30 | |
31 | #include <linux/firmware.h> |
32 | #include <linux/crc32.h> |
33 | |
34 | #include "i915_drv.h" |
35 | #include "gvt.h" |
36 | #include "i915_pvinfo.h" |
37 | |
38 | #define FIRMWARE_VERSION (0x0) |
39 | |
40 | struct { |
41 | u64 ; |
42 | u32 ; /* protect the data after this field */ |
43 | u32 ; |
44 | u64 ; |
45 | u64 ; /* offset in the file */ |
46 | u64 ; |
47 | u64 ; /* offset in the file */ |
48 | unsigned char []; |
49 | }; |
50 | |
51 | #define dev_to_drm_minor(d) dev_get_drvdata((d)) |
52 | |
53 | static ssize_t |
54 | gvt_firmware_read(struct file *filp, struct kobject *kobj, |
55 | struct bin_attribute *attr, char *buf, |
56 | loff_t offset, size_t count) |
57 | { |
58 | memcpy(buf, attr->private + offset, count); |
59 | return count; |
60 | } |
61 | |
62 | static struct bin_attribute firmware_attr = { |
63 | .attr = {.name = "gvt_firmware" , .mode = (S_IRUSR)}, |
64 | .read = gvt_firmware_read, |
65 | .write = NULL, |
66 | .mmap = NULL, |
67 | }; |
68 | |
69 | static int expose_firmware_sysfs(struct intel_gvt *gvt) |
70 | { |
71 | struct intel_gvt_device_info *info = &gvt->device_info; |
72 | struct drm_i915_private *i915 = gvt->gt->i915; |
73 | struct pci_dev *pdev = to_pci_dev(i915->drm.dev); |
74 | struct gvt_firmware_header *h; |
75 | void *firmware; |
76 | void *p; |
77 | unsigned long size, crc32_start; |
78 | int ret; |
79 | |
80 | size = offsetof(struct gvt_firmware_header, data) + info->mmio_size + info->cfg_space_size; |
81 | firmware = vzalloc(size); |
82 | if (!firmware) |
83 | return -ENOMEM; |
84 | |
85 | h = firmware; |
86 | |
87 | h->magic = VGT_MAGIC; |
88 | h->version = FIRMWARE_VERSION; |
89 | h->cfg_space_size = info->cfg_space_size; |
90 | h->cfg_space_offset = offsetof(struct gvt_firmware_header, data); |
91 | h->mmio_size = info->mmio_size; |
92 | h->mmio_offset = h->cfg_space_offset + h->cfg_space_size; |
93 | |
94 | p = firmware + h->cfg_space_offset; |
95 | |
96 | memcpy(gvt->firmware.cfg_space, i915->vgpu.initial_cfg_space, |
97 | info->cfg_space_size); |
98 | memcpy(p, gvt->firmware.cfg_space, info->cfg_space_size); |
99 | |
100 | p = firmware + h->mmio_offset; |
101 | |
102 | memcpy(gvt->firmware.mmio, i915->vgpu.initial_mmio, |
103 | info->mmio_size); |
104 | |
105 | memcpy(p, gvt->firmware.mmio, info->mmio_size); |
106 | |
107 | crc32_start = offsetof(struct gvt_firmware_header, version); |
108 | h->crc32 = crc32_le(crc: 0, p: firmware + crc32_start, len: size - crc32_start); |
109 | |
110 | firmware_attr.size = size; |
111 | firmware_attr.private = firmware; |
112 | |
113 | ret = device_create_bin_file(dev: &pdev->dev, attr: &firmware_attr); |
114 | if (ret) { |
115 | vfree(addr: firmware); |
116 | return ret; |
117 | } |
118 | return 0; |
119 | } |
120 | |
121 | static void clean_firmware_sysfs(struct intel_gvt *gvt) |
122 | { |
123 | struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev); |
124 | |
125 | device_remove_bin_file(dev: &pdev->dev, attr: &firmware_attr); |
126 | vfree(addr: firmware_attr.private); |
127 | } |
128 | |
129 | /** |
130 | * intel_gvt_free_firmware - free GVT firmware |
131 | * @gvt: intel gvt device |
132 | * |
133 | */ |
134 | void intel_gvt_free_firmware(struct intel_gvt *gvt) |
135 | { |
136 | if (!gvt->firmware.firmware_loaded) |
137 | clean_firmware_sysfs(gvt); |
138 | |
139 | kfree(objp: gvt->firmware.cfg_space); |
140 | vfree(addr: gvt->firmware.mmio); |
141 | } |
142 | |
143 | static int verify_firmware(struct intel_gvt *gvt, |
144 | const struct firmware *fw) |
145 | { |
146 | struct intel_gvt_device_info *info = &gvt->device_info; |
147 | struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev); |
148 | struct gvt_firmware_header *h; |
149 | unsigned long id, crc32_start; |
150 | const void *mem; |
151 | const char *item; |
152 | u64 file, request; |
153 | |
154 | h = (struct gvt_firmware_header *)fw->data; |
155 | |
156 | crc32_start = offsetofend(struct gvt_firmware_header, crc32); |
157 | mem = fw->data + crc32_start; |
158 | |
159 | #define VERIFY(s, a, b) do { \ |
160 | item = (s); file = (u64)(a); request = (u64)(b); \ |
161 | if ((a) != (b)) \ |
162 | goto invalid_firmware; \ |
163 | } while (0) |
164 | |
165 | VERIFY("magic number" , h->magic, VGT_MAGIC); |
166 | VERIFY("version" , h->version, FIRMWARE_VERSION); |
167 | VERIFY("crc32" , h->crc32, crc32_le(0, mem, fw->size - crc32_start)); |
168 | VERIFY("cfg space size" , h->cfg_space_size, info->cfg_space_size); |
169 | VERIFY("mmio size" , h->mmio_size, info->mmio_size); |
170 | |
171 | mem = (fw->data + h->cfg_space_offset); |
172 | |
173 | id = *(u16 *)(mem + PCI_VENDOR_ID); |
174 | VERIFY("vendor id" , id, pdev->vendor); |
175 | |
176 | id = *(u16 *)(mem + PCI_DEVICE_ID); |
177 | VERIFY("device id" , id, pdev->device); |
178 | |
179 | id = *(u8 *)(mem + PCI_REVISION_ID); |
180 | VERIFY("revision id" , id, pdev->revision); |
181 | |
182 | #undef VERIFY |
183 | return 0; |
184 | |
185 | invalid_firmware: |
186 | gvt_dbg_core("Invalid firmware: %s [file] 0x%llx [request] 0x%llx\n" , |
187 | item, file, request); |
188 | return -EINVAL; |
189 | } |
190 | |
191 | #define GVT_FIRMWARE_PATH "i915/gvt" |
192 | |
193 | /** |
194 | * intel_gvt_load_firmware - load GVT firmware |
195 | * @gvt: intel gvt device |
196 | * |
197 | */ |
198 | int intel_gvt_load_firmware(struct intel_gvt *gvt) |
199 | { |
200 | struct intel_gvt_device_info *info = &gvt->device_info; |
201 | struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev); |
202 | struct intel_gvt_firmware *firmware = &gvt->firmware; |
203 | struct gvt_firmware_header *h; |
204 | const struct firmware *fw; |
205 | char *path; |
206 | void *mem; |
207 | int ret; |
208 | |
209 | path = kmalloc(PATH_MAX, GFP_KERNEL); |
210 | if (!path) |
211 | return -ENOMEM; |
212 | |
213 | mem = kmalloc(size: info->cfg_space_size, GFP_KERNEL); |
214 | if (!mem) { |
215 | kfree(objp: path); |
216 | return -ENOMEM; |
217 | } |
218 | |
219 | firmware->cfg_space = mem; |
220 | |
221 | mem = vmalloc(size: info->mmio_size); |
222 | if (!mem) { |
223 | kfree(objp: path); |
224 | kfree(objp: firmware->cfg_space); |
225 | return -ENOMEM; |
226 | } |
227 | |
228 | firmware->mmio = mem; |
229 | |
230 | sprintf(buf: path, fmt: "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state" , |
231 | GVT_FIRMWARE_PATH, pdev->vendor, pdev->device, |
232 | pdev->revision); |
233 | |
234 | gvt_dbg_core("request hw state firmware %s...\n" , path); |
235 | |
236 | ret = request_firmware(fw: &fw, name: path, device: gvt->gt->i915->drm.dev); |
237 | kfree(objp: path); |
238 | |
239 | if (ret) |
240 | goto expose_firmware; |
241 | |
242 | gvt_dbg_core("success.\n" ); |
243 | |
244 | ret = verify_firmware(gvt, fw); |
245 | if (ret) |
246 | goto out_free_fw; |
247 | |
248 | gvt_dbg_core("verified.\n" ); |
249 | |
250 | h = (struct gvt_firmware_header *)fw->data; |
251 | |
252 | memcpy(firmware->cfg_space, fw->data + h->cfg_space_offset, |
253 | h->cfg_space_size); |
254 | memcpy(firmware->mmio, fw->data + h->mmio_offset, |
255 | h->mmio_size); |
256 | |
257 | release_firmware(fw); |
258 | firmware->firmware_loaded = true; |
259 | return 0; |
260 | |
261 | out_free_fw: |
262 | release_firmware(fw); |
263 | expose_firmware: |
264 | expose_firmware_sysfs(gvt); |
265 | return 0; |
266 | } |
267 | |