1 // Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /*
6 * The purpose of this test is to exercise the GPU failure path.
7 * We craft an erroneous GPU command packet and send it to the GPU,
8 * and wait for a udev event notifying us of a GPU hang.
9 * If the event doesn't come back, the test fails.
10 *
11 * This test must run with ui stopped.
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17
OUTPUT_INFO(char * msg)18 void OUTPUT_INFO(char *msg) {
19 printf("INFO: %s\n", msg);
20 fflush(0);
21 }
OUTPUT_WARNING(char * msg)22 void OUTPUT_WARNING(char *msg) {
23 printf("WARNING: %s\n", msg);
24 fflush(0);
25 }
OUTPUT_ERROR(char * msg)26 void OUTPUT_ERROR(char *msg) {
27 printf("ERROR: %s\n", msg);
28 fflush(0);
29 }
OUTPUT_RUN()30 void OUTPUT_RUN() {
31 printf("[ RUN ] graphics_GpuReset\n");
32 fflush(0);
33 }
EXIT(int code)34 void EXIT(int code) {
35 // Sleep a bit. This is not strictly required but will avoid the case where
36 // we call the test back to back and the kernel thinks the GPU is toast.
37 OUTPUT_INFO("sleep(10) to prevent the kernel from thinking the GPU is completely locked.");
38 sleep(10);
39 exit(code);
40 }
OUTPUT_PASS_AND_EXIT()41 void OUTPUT_PASS_AND_EXIT() {
42 printf("[ OK ] graphics_GpuReset\n");
43 fflush(0);
44 EXIT(0);
45 }
OUTPUT_FAIL_AND_EXIT(char * msg)46 void OUTPUT_FAIL_AND_EXIT(char *msg) {
47 printf("[ FAILED ] graphics_GpuReset %s\n", msg);
48 fflush(0);
49 EXIT(-1);
50 }
51
52 #if !defined(__INTEL_GPU__)
53
54 #pragma message "Compiling for GPU other than Intel."
55
main(int argc,char ** argv)56 int main(int argc, char **argv)
57 {
58 OUTPUT_RUN();
59 OUTPUT_WARNING("The gpureset test is defined for some Intel GPUs only.");
60 OUTPUT_PASS_AND_EXIT();
61 return 0;
62 }
63
64 #else
65
66 #pragma message "Compiling for Intel GPU."
67
68 #include <assert.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <fnmatch.h>
72 #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
73 #include <libudev.h>
74 #include <stdbool.h>
75 #include <string.h>
76 #include <sys/ioctl.h>
77 #include <sys/types.h>
78 #include <sys/select.h>
79 #include <sys/stat.h>
80
81 #include "xf86drm.h"
82 #include "i915_drm.h"
83 #include "intel_bufmgr.h"
84
85 #define DRM_TEST_MASTER 0x01
86
87
is_master(int fd)88 static int is_master(int fd)
89 {
90 drm_client_t client;
91 int ret;
92
93 /* Check that we're the only opener and authed. */
94 client.idx = 0;
95 ret = ioctl(fd, DRM_IOCTL_GET_CLIENT, &client);
96 assert (ret == 0);
97 if (!client.auth)
98 return 0;
99 client.idx = 1;
100 ret = ioctl(fd, DRM_IOCTL_GET_CLIENT, &client);
101 if (ret != -1 || errno != EINVAL)
102 return 0;
103
104 return 1;
105 }
106
107 /** Open the first DRM device matching the criteria. */
drm_open_matching(const char * pci_glob,int flags)108 int drm_open_matching(const char *pci_glob, int flags)
109 {
110 struct udev *udev;
111 struct udev_enumerate *e;
112 struct udev_device *device, *parent;
113 struct udev_list_entry *entry;
114 const char *pci_id, *path;
115 const char *usub, *dnode;
116 int fd;
117
118 udev = udev_new();
119 if (udev == NULL)
120 return -1;
121
122 fd = -1;
123 e = udev_enumerate_new(udev);
124 udev_enumerate_add_match_subsystem(e, "drm");
125 udev_enumerate_scan_devices(e);
126 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
127 path = udev_list_entry_get_name(entry);
128 device = udev_device_new_from_syspath(udev, path);
129 parent = udev_device_get_parent(device);
130 usub = udev_device_get_subsystem(parent);
131 /* Filter out KMS output devices. */
132 if (!usub || (strcmp(usub, "pci") != 0))
133 continue;
134 pci_id = udev_device_get_property_value(parent, "PCI_ID");
135 if (fnmatch(pci_glob, pci_id, 0) != 0)
136 continue;
137 dnode = udev_device_get_devnode(device);
138 if (strstr(dnode, "control"))
139 continue;
140 fd = open(dnode, O_RDWR);
141 if (fd < 0)
142 continue;
143 if ((flags & DRM_TEST_MASTER) && !is_master(fd)) {
144 close(fd);
145 fd = -1;
146 continue;
147 }
148
149 break;
150 }
151 udev_enumerate_unref(e);
152 udev_unref(udev);
153
154 return fd;
155 }
156
udev_init()157 struct udev_monitor* udev_init()
158 {
159 char* subsystem = "drm";
160 struct udev* udev;
161 // Create the udev object.
162 udev = udev_new();
163 if (!udev) {
164 OUTPUT_ERROR("Can't get create udev object.");
165 return NULL;
166 }
167
168 // Create the udev monitor structure.
169 struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev");
170 if (!monitor) {
171 OUTPUT_ERROR("Can't get create udev monitor");
172 udev_unref(udev);
173 return NULL;
174 }
175
176 udev_monitor_filter_add_match_subsystem_devtype(monitor,
177 subsystem,
178 NULL);
179 udev_monitor_enable_receiving(monitor);
180
181 return monitor;
182 }
183
udev_wait(struct udev_monitor * monitor)184 int udev_wait(struct udev_monitor* monitor)
185 {
186 fd_set fds;
187 struct timeval tv;
188 int ret;
189
190 int fd = udev_monitor_get_fd(monitor);
191
192 FD_ZERO(&fds);
193 FD_SET(fd, &fds);
194
195 // Wait for at most 20 seconds for the event to come back.
196 tv.tv_sec = 20;
197 tv.tv_usec = 0;
198
199 ret = select(fd+1, &fds, NULL, NULL, &tv);
200
201 if (ret>0)
202 {
203 struct udev_device* dev = udev_monitor_receive_device(monitor);
204 if (dev) {
205 // TODO(ihf): variable args to INFO function.
206 printf("INFO: Event on (%s|%s|%s) Action %s\n",
207 udev_device_get_devnode(dev),
208 udev_device_get_subsystem(dev),
209 udev_device_get_devtype(dev),
210 udev_device_get_action(dev));
211 udev_device_unref(dev);
212 return 1;
213 } else {
214 OUTPUT_ERROR("Can't get receive_device().");
215 return 0;
216 }
217 } else {
218 OUTPUT_ERROR("Timed out waiting for udev event to come back.");
219 return 0;
220 }
221 }
222
main(int argc,char ** argv)223 int main(int argc, char **argv)
224 {
225 int fd;
226 int ret;
227 drmVersionPtr v;
228
229 OUTPUT_RUN();
230 OUTPUT_INFO("The GPU reset test *must* be run with 'stop ui'.");
231 OUTPUT_INFO("Otherwise following tests will likely hang/crash the machine.");
232 OUTPUT_INFO("sleep(10) to make sure UI has time to stop.");
233 sleep(10);
234
235 fd = drm_open_matching("*:*", 0);
236
237 if (fd < 0) {
238 OUTPUT_FAIL_AND_EXIT("Failed to open any drm device.");
239 }
240
241 v = drmGetVersion(fd);
242 assert(strlen(v->name) != 0);
243 if (strcmp(v->name, "i915") == 0) {
244 assert(v->version_major >= 1);
245 } else {
246 OUTPUT_WARNING("Can't find Intel GPU.");
247 OUTPUT_PASS_AND_EXIT();
248 }
249
250 unsigned int pci_id;
251 struct drm_i915_getparam gp;
252 gp.param = I915_PARAM_CHIPSET_ID;
253 gp.value = (int*)&pci_id;
254 ret = ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp));
255
256 if (ret) {
257 OUTPUT_FAIL_AND_EXIT("Can't get the i915 pci_id.");
258 }
259
260 // TODO(ihf): variable args to INFO function.
261 printf("INFO: i915 pci_id=0x%x.\n", pci_id);
262 switch(pci_id) {
263 // sandy bridge
264 case 0x102:
265 case 0x106: // Butterfly, Lumpy.
266 case 0x116:
267 case 0x126:
268 // ivy bridge
269 case 0x156: // Stout.
270 case 0x166: // Link.
271 // haswell
272 case 0xa06: // GT1, Peppy, Falco.
273 case 0xa16: // GT2.
274 case 0xa26: // GT3.
275 break;
276 default:
277 {
278 OUTPUT_WARNING("Intel GPU detected, but model doesn't support reset.");
279 OUTPUT_PASS_AND_EXIT();
280 }
281 }
282
283 struct udev_monitor* monitor = udev_init();
284 if (!monitor) {
285 OUTPUT_FAIL_AND_EXIT("udev init failed.");
286 }
287
288 drm_intel_bufmgr* bufmgr = drm_intel_bufmgr_gem_init(fd, 4096);
289
290 drm_intel_bo* bo;
291 bo = drm_intel_bo_alloc(bufmgr, "bogus cmdbuffer", 4096, 4096);
292
293 uint32_t invalid_buf[8] =
294 {
295 0x00000000, // NOOP
296 0xd00dd00d, // invalid command
297 0x00000000, // NOOP
298 0x00000000, // NOOP
299 0x05000000, // BATCHBUFFER_END
300 0x05000000, // BATCHBUFFER_END
301 0x05000000, // BATCHBUFFER_END
302 0x05000000, // BATCHBUFFER_END
303 };
304
305 // Copy our invalid cmd buffer into the bo.
306 ret = drm_intel_bo_subdata(bo, 0, sizeof(invalid_buf), invalid_buf);
307 if (ret != 0) {
308 OUTPUT_FAIL_AND_EXIT("bo_subdata failed.");
309 }
310
311 // Submit our invalid buffer.
312 ret = drm_intel_bo_exec(bo, sizeof(invalid_buf), NULL, 0, 0);
313 if (ret != 0) {
314 OUTPUT_FAIL_AND_EXIT("bo_exec failed.");
315 }
316 OUTPUT_INFO("Sent bogus buffer, waiting for event.");
317 // Submit our invalid buffer.
318 drm_intel_bo_wait_rendering(bo);
319
320 int res = udev_wait(monitor);
321
322 drmFree(v);
323 close(fd);
324
325 if (res) {
326 OUTPUT_PASS_AND_EXIT();
327 }
328 else {
329 OUTPUT_FAIL_AND_EXIT("GPU reset event did not come back.");
330 }
331
332 return 0;
333 }
334
335 #endif // defined(__arm__) || !defined(__INTEL_GPU__)
336