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