1 /*
2 * Copyright (c) 2016, NVIDIA CORPORATION.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and/or associated documentation files (the
6 * "Materials"), to deal in the Materials without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Materials, and to
9 * permit persons to whom the Materials are furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * unaltered in all copies or substantial portions of the Materials.
14 * Any additions, deletions, or changes to the original source files
15 * must be clearly indicated in accompanying documentation.
16 *
17 * If only executable code is distributed, then the accompanying
18 * documentation must state that "this software is based in part on the
19 * work of the Khronos Group."
20 *
21 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
28 */
29
30 #include <dix-config.h>
31
32 #include "hashtable.h"
33 #include "vndserver.h"
34 #include "vndservervendor.h"
35
36 /**
37 * The length of the dispatchFuncs array. Every opcode above this is a
38 * X_GLsop_* code, which all can use the same handler.
39 */
40 #define OPCODE_ARRAY_LEN 100
41
42 // This hashtable is used to keep track of the dispatch stubs for
43 // GLXVendorPrivate and GLXVendorPrivateWithReply.
44 typedef struct GlxVendorPrivDispatchRec {
45 CARD32 vendorCode;
46 GlxServerDispatchProc proc;
47 HashTable hh;
48 } GlxVendorPrivDispatch;
49
50 static GlxServerDispatchProc dispatchFuncs[OPCODE_ARRAY_LEN] = {};
51 static HashTable vendorPrivHash = NULL;
52 static HtGenericHashSetupRec vendorPrivSetup = {
53 .keySize = sizeof(CARD32)
54 };
55
DispatchBadRequest(ClientPtr client)56 static int DispatchBadRequest(ClientPtr client)
57 {
58 return BadRequest;
59 }
60
LookupVendorPrivDispatch(CARD32 vendorCode,Bool create)61 static GlxVendorPrivDispatch *LookupVendorPrivDispatch(CARD32 vendorCode, Bool create)
62 {
63 GlxVendorPrivDispatch *disp = NULL;
64
65 disp = ht_find(vendorPrivHash, &vendorCode);
66 if (disp == NULL && create) {
67 if ((disp = ht_add(vendorPrivHash, &vendorCode))) {
68 disp->vendorCode = vendorCode;
69 disp->proc = NULL;
70 }
71 }
72
73 return disp;
74 }
75
GetVendorDispatchFunc(CARD8 opcode,CARD32 vendorCode)76 static GlxServerDispatchProc GetVendorDispatchFunc(CARD8 opcode, CARD32 vendorCode)
77 {
78 GlxServerVendor *vendor;
79
80 xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
81 GlxServerDispatchProc proc = vendor->glxvc.getDispatchAddress(opcode, vendorCode);
82 if (proc != NULL) {
83 return proc;
84 }
85 }
86
87 return DispatchBadRequest;
88 }
89
SetReplyHeader(ClientPtr client,void * replyPtr)90 static void SetReplyHeader(ClientPtr client, void *replyPtr)
91 {
92 xGenericReply *rep = (xGenericReply *) replyPtr;
93 rep->type = X_Reply;
94 rep->sequenceNumber = client->sequence;
95 rep->length = 0;
96 }
97
98 /* Include the trivial dispatch handlers */
99 #include "vnd_dispatch_stubs.c"
100
dispatch_GLXQueryVersion(ClientPtr client)101 static int dispatch_GLXQueryVersion(ClientPtr client)
102 {
103 xGLXQueryVersionReply reply;
104 REQUEST_SIZE_MATCH(xGLXQueryVersionReq);
105
106 SetReplyHeader(client, &reply);
107 reply.majorVersion = GlxCheckSwap(client, 1);
108 reply.minorVersion = GlxCheckSwap(client, 4);
109
110 WriteToClient(client, sz_xGLXQueryVersionReply, &reply);
111 return Success;
112 }
113
114 /* broken header workaround */
115 #ifndef X_GLXSetClientInfo2ARB
116 #define X_GLXSetClientInfo2ARB X_GLXSetConfigInfo2ARB
117 #endif
118
119 /**
120 * This function is used for X_GLXClientInfo, X_GLXSetClientInfoARB, and
121 * X_GLXSetClientInfo2ARB.
122 */
dispatch_GLXClientInfo(ClientPtr client)123 static int dispatch_GLXClientInfo(ClientPtr client)
124 {
125 GlxServerVendor *vendor;
126 void *requestCopy = NULL;
127 size_t requestSize = client->req_len * 4;
128
129 if (client->minorOp == X_GLXClientInfo) {
130 REQUEST_AT_LEAST_SIZE(xGLXClientInfoReq);
131 } else if (client->minorOp == X_GLXSetClientInfoARB) {
132 REQUEST_AT_LEAST_SIZE(xGLXSetClientInfoARBReq);
133 } else if (client->minorOp == X_GLXSetClientInfo2ARB) {
134 REQUEST_AT_LEAST_SIZE(xGLXSetClientInfo2ARBReq);
135 } else {
136 return BadImplementation;
137 }
138
139 // We'll forward this request to each vendor library. Since a vendor might
140 // modify the request data in place (e.g., for byte swapping), make a copy
141 // of the request first.
142 requestCopy = malloc(requestSize);
143 if (requestCopy == NULL) {
144 return BadAlloc;
145 }
146 memcpy(requestCopy, client->requestBuffer, requestSize);
147
148 xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
149 vendor->glxvc.handleRequest(client);
150 // Revert the request buffer back to our copy.
151 memcpy(client->requestBuffer, requestCopy, requestSize);
152 }
153 free(requestCopy);
154 return Success;
155 }
156
CommonLoseCurrent(ClientPtr client,GlxContextTagInfo * tagInfo)157 static int CommonLoseCurrent(ClientPtr client, GlxContextTagInfo *tagInfo)
158 {
159 int ret;
160
161 ret = tagInfo->vendor->glxvc.makeCurrent(client,
162 tagInfo->tag, // No old context tag,
163 None, None, None, 0);
164
165 if (ret == Success) {
166 GlxFreeContextTag(tagInfo);
167 }
168 return ret;
169 }
170
CommonMakeNewCurrent(ClientPtr client,GlxServerVendor * vendor,GLXDrawable drawable,GLXDrawable readdrawable,GLXContextID context,GLXContextTag * newContextTag)171 static int CommonMakeNewCurrent(ClientPtr client,
172 GlxServerVendor *vendor,
173 GLXDrawable drawable,
174 GLXDrawable readdrawable,
175 GLXContextID context,
176 GLXContextTag *newContextTag)
177 {
178 int ret = BadAlloc;
179 GlxContextTagInfo *tagInfo;
180
181 tagInfo = GlxAllocContextTag(client, vendor);
182
183 if (tagInfo) {
184 ret = vendor->glxvc.makeCurrent(client,
185 0, // No old context tag,
186 drawable, readdrawable, context,
187 tagInfo->tag);
188
189 if (ret == Success) {
190 tagInfo->drawable = drawable;
191 tagInfo->readdrawable = readdrawable;
192 tagInfo->context = context;
193 *newContextTag = tagInfo->tag;
194 } else {
195 GlxFreeContextTag(tagInfo);
196 }
197 }
198
199 return ret;
200 }
201
CommonMakeCurrent(ClientPtr client,GLXContextTag oldContextTag,GLXDrawable drawable,GLXDrawable readdrawable,GLXContextID context)202 static int CommonMakeCurrent(ClientPtr client,
203 GLXContextTag oldContextTag,
204 GLXDrawable drawable,
205 GLXDrawable readdrawable,
206 GLXContextID context)
207 {
208 xGLXMakeCurrentReply reply = {};
209 GlxContextTagInfo *oldTag = NULL;
210 GlxServerVendor *newVendor = NULL;
211
212 oldContextTag = GlxCheckSwap(client, oldContextTag);
213 drawable = GlxCheckSwap(client, drawable);
214 readdrawable = GlxCheckSwap(client, readdrawable);
215 context = GlxCheckSwap(client, context);
216
217 SetReplyHeader(client, &reply);
218
219 if (oldContextTag != 0) {
220 oldTag = GlxLookupContextTag(client, oldContextTag);
221 if (oldTag == NULL) {
222 return GlxErrorBase + GLXBadContextTag;
223 }
224 }
225 if (context != 0) {
226 newVendor = GlxGetXIDMap(context);
227 if (newVendor == NULL) {
228 return GlxErrorBase + GLXBadContext;
229 }
230 }
231
232 if (oldTag == NULL && newVendor == NULL) {
233 // Nothing to do here. Just send a successful reply.
234 reply.contextTag = 0;
235 } else if (oldTag != NULL && newVendor != NULL
236 && oldTag->context == context
237 && oldTag->drawable == drawable
238 && oldTag->readdrawable == readdrawable)
239 {
240 // The old and new values are all the same, so send a successful reply.
241 reply.contextTag = oldTag->tag;
242 } else {
243 // TODO: For switching contexts in a single vendor, just make one
244 // makeCurrent call?
245
246 // TODO: When changing vendors, would it be better to do the
247 // MakeCurrent(new) first, then the LoseCurrent(old)?
248 // If the MakeCurrent(new) fails, then the old context will still be current.
249 // If the LoseCurrent(old) fails, then we can (probably) undo the MakeCurrent(new) with
250 // a LoseCurrent(old).
251 // But, if the recovery LoseCurrent(old) fails, then we're really in a bad state.
252
253 // Clear the old context first.
254 if (oldTag != NULL) {
255 int ret = CommonLoseCurrent(client, oldTag);
256 if (ret != Success) {
257 return ret;
258 }
259 oldTag = NULL;
260 }
261
262 if (newVendor != NULL) {
263 int ret = CommonMakeNewCurrent(client, newVendor, drawable, readdrawable, context, &reply.contextTag);
264 if (ret != Success) {
265 return ret;
266 }
267 } else {
268 reply.contextTag = 0;
269 }
270 }
271
272 reply.contextTag = GlxCheckSwap(client, reply.contextTag);
273 WriteToClient(client, sz_xGLXMakeCurrentReply, &reply);
274 return Success;
275 }
276
dispatch_GLXMakeCurrent(ClientPtr client)277 static int dispatch_GLXMakeCurrent(ClientPtr client)
278 {
279 REQUEST(xGLXMakeCurrentReq);
280 REQUEST_SIZE_MATCH(*stuff);
281
282 return CommonMakeCurrent(client, stuff->oldContextTag,
283 stuff->drawable, stuff->drawable, stuff->context);
284 }
285
dispatch_GLXMakeContextCurrent(ClientPtr client)286 static int dispatch_GLXMakeContextCurrent(ClientPtr client)
287 {
288 REQUEST(xGLXMakeContextCurrentReq);
289 REQUEST_SIZE_MATCH(*stuff);
290
291 return CommonMakeCurrent(client, stuff->oldContextTag,
292 stuff->drawable, stuff->readdrawable, stuff->context);
293 }
294
dispatch_GLXMakeCurrentReadSGI(ClientPtr client)295 static int dispatch_GLXMakeCurrentReadSGI(ClientPtr client)
296 {
297 REQUEST(xGLXMakeCurrentReadSGIReq);
298 REQUEST_SIZE_MATCH(*stuff);
299
300 return CommonMakeCurrent(client, stuff->oldContextTag,
301 stuff->drawable, stuff->readable, stuff->context);
302 }
303
dispatch_GLXCopyContext(ClientPtr client)304 static int dispatch_GLXCopyContext(ClientPtr client)
305 {
306 REQUEST(xGLXCopyContextReq);
307 GlxServerVendor *vendor;
308 REQUEST_SIZE_MATCH(*stuff);
309
310 // If we've got a context tag, then we'll use it to select a vendor. If we
311 // don't have a tag, then we'll look up one of the contexts. In either
312 // case, it's up to the vendor library to make sure that the context ID's
313 // are valid.
314 if (stuff->contextTag != 0) {
315 GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
316 if (tagInfo == NULL) {
317 return GlxErrorBase + GLXBadContextTag;
318 }
319 vendor = tagInfo->vendor;
320 } else {
321 vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->source));
322 if (vendor == NULL) {
323 return GlxErrorBase + GLXBadContext;
324 }
325 }
326 return vendor->glxvc.handleRequest(client);
327 }
328
dispatch_GLXSwapBuffers(ClientPtr client)329 static int dispatch_GLXSwapBuffers(ClientPtr client)
330 {
331 GlxServerVendor *vendor = NULL;
332 REQUEST(xGLXSwapBuffersReq);
333 REQUEST_SIZE_MATCH(*stuff);
334
335 if (stuff->contextTag != 0) {
336 // If the request has a context tag, then look up a vendor from that.
337 // The vendor library is then responsible for validating the drawable.
338 GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
339 if (tagInfo == NULL) {
340 return GlxErrorBase + GLXBadContextTag;
341 }
342 vendor = tagInfo->vendor;
343 } else {
344 // We don't have a context tag, so look up the vendor from the
345 // drawable.
346 vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->drawable));
347 if (vendor == NULL) {
348 return GlxErrorBase + GLXBadDrawable;
349 }
350 }
351
352 return vendor->glxvc.handleRequest(client);
353 }
354
355 /**
356 * This is a generic handler for all of the X_GLXsop* requests.
357 */
dispatch_GLXSingle(ClientPtr client)358 static int dispatch_GLXSingle(ClientPtr client)
359 {
360 REQUEST(xGLXSingleReq);
361 GlxContextTagInfo *tagInfo;
362 REQUEST_AT_LEAST_SIZE(*stuff);
363
364 tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
365 if (tagInfo != NULL) {
366 return tagInfo->vendor->glxvc.handleRequest(client);
367 } else {
368 return GlxErrorBase + GLXBadContextTag;
369 }
370 }
371
dispatch_GLXVendorPriv(ClientPtr client)372 static int dispatch_GLXVendorPriv(ClientPtr client)
373 {
374 GlxVendorPrivDispatch *disp;
375 REQUEST(xGLXVendorPrivateReq);
376 REQUEST_AT_LEAST_SIZE(*stuff);
377
378 disp = LookupVendorPrivDispatch(GlxCheckSwap(client, stuff->vendorCode), TRUE);
379 if (disp == NULL) {
380 return BadAlloc;
381 }
382
383 if (disp->proc == NULL) {
384 // We don't have a dispatch function for this request yet. Check with
385 // each vendor library to find one.
386 // Note that even if none of the vendors provides a dispatch stub,
387 // we'll still add an entry to the dispatch table, so that we don't
388 // have to look it up again later.
389
390 disp->proc = GetVendorDispatchFunc(stuff->glxCode,
391 GlxCheckSwap(client,
392 stuff->vendorCode));
393 }
394 return disp->proc(client);
395 }
396
GlxDispatchInit(void)397 Bool GlxDispatchInit(void)
398 {
399 GlxVendorPrivDispatch *disp;
400
401 vendorPrivHash = ht_create(sizeof(CARD32), sizeof(GlxVendorPrivDispatch),
402 ht_generic_hash, ht_generic_compare,
403 (void *) &vendorPrivSetup);
404 if (!vendorPrivHash) {
405 return FALSE;
406 }
407
408 // Assign a custom dispatch stub GLXMakeCurrentReadSGI. This is the only
409 // vendor private request that we need to deal with in libglvnd itself.
410 disp = LookupVendorPrivDispatch(X_GLXvop_MakeCurrentReadSGI, TRUE);
411 if (disp == NULL) {
412 return FALSE;
413 }
414 disp->proc = dispatch_GLXMakeCurrentReadSGI;
415
416 // Assign the dispatch stubs for requests that need special handling.
417 dispatchFuncs[X_GLXQueryVersion] = dispatch_GLXQueryVersion;
418 dispatchFuncs[X_GLXMakeCurrent] = dispatch_GLXMakeCurrent;
419 dispatchFuncs[X_GLXMakeContextCurrent] = dispatch_GLXMakeContextCurrent;
420 dispatchFuncs[X_GLXCopyContext] = dispatch_GLXCopyContext;
421 dispatchFuncs[X_GLXSwapBuffers] = dispatch_GLXSwapBuffers;
422
423 dispatchFuncs[X_GLXClientInfo] = dispatch_GLXClientInfo;
424 dispatchFuncs[X_GLXSetClientInfoARB] = dispatch_GLXClientInfo;
425 dispatchFuncs[X_GLXSetClientInfo2ARB] = dispatch_GLXClientInfo;
426
427 dispatchFuncs[X_GLXVendorPrivate] = dispatch_GLXVendorPriv;
428 dispatchFuncs[X_GLXVendorPrivateWithReply] = dispatch_GLXVendorPriv;
429
430 // Assign the trivial stubs
431 dispatchFuncs[X_GLXRender] = dispatch_Render;
432 dispatchFuncs[X_GLXRenderLarge] = dispatch_RenderLarge;
433 dispatchFuncs[X_GLXCreateContext] = dispatch_CreateContext;
434 dispatchFuncs[X_GLXDestroyContext] = dispatch_DestroyContext;
435 dispatchFuncs[X_GLXWaitGL] = dispatch_WaitGL;
436 dispatchFuncs[X_GLXWaitX] = dispatch_WaitX;
437 dispatchFuncs[X_GLXUseXFont] = dispatch_UseXFont;
438 dispatchFuncs[X_GLXCreateGLXPixmap] = dispatch_CreateGLXPixmap;
439 dispatchFuncs[X_GLXGetVisualConfigs] = dispatch_GetVisualConfigs;
440 dispatchFuncs[X_GLXDestroyGLXPixmap] = dispatch_DestroyGLXPixmap;
441 dispatchFuncs[X_GLXQueryExtensionsString] = dispatch_QueryExtensionsString;
442 dispatchFuncs[X_GLXQueryServerString] = dispatch_QueryServerString;
443 dispatchFuncs[X_GLXChangeDrawableAttributes] = dispatch_ChangeDrawableAttributes;
444 dispatchFuncs[X_GLXCreateNewContext] = dispatch_CreateNewContext;
445 dispatchFuncs[X_GLXCreatePbuffer] = dispatch_CreatePbuffer;
446 dispatchFuncs[X_GLXCreatePixmap] = dispatch_CreatePixmap;
447 dispatchFuncs[X_GLXCreateWindow] = dispatch_CreateWindow;
448 dispatchFuncs[X_GLXCreateContextAttribsARB] = dispatch_CreateContextAttribsARB;
449 dispatchFuncs[X_GLXDestroyPbuffer] = dispatch_DestroyPbuffer;
450 dispatchFuncs[X_GLXDestroyPixmap] = dispatch_DestroyPixmap;
451 dispatchFuncs[X_GLXDestroyWindow] = dispatch_DestroyWindow;
452 dispatchFuncs[X_GLXGetDrawableAttributes] = dispatch_GetDrawableAttributes;
453 dispatchFuncs[X_GLXGetFBConfigs] = dispatch_GetFBConfigs;
454 dispatchFuncs[X_GLXQueryContext] = dispatch_QueryContext;
455 dispatchFuncs[X_GLXIsDirect] = dispatch_IsDirect;
456
457 return TRUE;
458 }
459
GlxDispatchReset(void)460 void GlxDispatchReset(void)
461 {
462 memset(dispatchFuncs, 0, sizeof(dispatchFuncs));
463
464 ht_destroy(vendorPrivHash);
465 vendorPrivHash = NULL;
466 }
467
GlxDispatchRequest(ClientPtr client)468 int GlxDispatchRequest(ClientPtr client)
469 {
470 REQUEST(xReq);
471 int result;
472
473 if (GlxExtensionEntry->base == 0)
474 return BadRequest;
475
476 GlxSetRequestClient(client);
477
478 if (stuff->data < OPCODE_ARRAY_LEN) {
479 if (dispatchFuncs[stuff->data] == NULL) {
480 // Try to find a dispatch stub.
481 dispatchFuncs[stuff->data] = GetVendorDispatchFunc(stuff->data, 0);
482 }
483 result = dispatchFuncs[stuff->data](client);
484 } else {
485 result = dispatch_GLXSingle(client);
486 }
487
488 GlxSetRequestClient(NULL);
489
490 return result;
491 }
492