icd: add loader magic word to verify ICD is compatible
We wanted a more explicit way to determine if the driver
ICD being loaded is providing compatible objects. To do
that we check for a magic dword value at the beginning
of the object. Non-compliant ICDs will assert in the loader
or the loader's dispatch functions if an object does
not have the correct value.
Dispatch checks are debug only.
diff --git a/icd/intel/gpu.c b/icd/intel/gpu.c
index 76ee90d..6cbb9d0 100644
--- a/icd/intel/gpu.c
+++ b/icd/intel/gpu.c
@@ -36,6 +36,7 @@
#include "queue.h"
#include "gpu.h"
#include "wsi_x11.h"
+#include "xglIcd.h"
static struct intel_gpu *intel_gpus;
@@ -125,6 +126,7 @@
return NULL;
memset(gpu, 0, sizeof(*gpu));
+ set_loader_magic_value(gpu);
gpu->devid = devid;
diff --git a/icd/intel/obj.c b/icd/intel/obj.c
index 8fc3f9d..c93f5f4 100644
--- a/icd/intel/obj.c
+++ b/icd/intel/obj.c
@@ -29,6 +29,7 @@
#include "gpu.h"
#include "mem.h"
#include "obj.h"
+#include "xglIcd.h"
static const uint32_t intel_base_magic = 0x494e544c;
@@ -385,6 +386,7 @@
return NULL;
memset(base, 0, obj_size);
+ set_loader_magic_value(base);
base->magic = intel_base_magic + type;
if (dev == NULL) {
diff --git a/include/xglIcd.h b/include/xglIcd.h
new file mode 100644
index 0000000..3efffe6
--- /dev/null
+++ b/include/xglIcd.h
@@ -0,0 +1,32 @@
+#ifndef XGLICD_H
+#define XGLICD_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "xglPlatform.h"
+
+/*
+ * The ICD must reserve space for a pointer for the loader's dispatch
+ * table, at the start of <each object>.
+ * The ICD must initialize this variable using the SET_LOADER_MAGIC_VALUE macro.
+ */
+
+#define ICD_LOADER_MAGIC 0x01CDC0DE
+
+typedef union _XGL_LOADER_DATA {
+ uint32_t loaderMagic;
+ void *loaderData;
+} XGL_LOADER_DATA;
+
+STATIC_INLINE void set_loader_magic_value(void *pNewObject) {
+ XGL_LOADER_DATA *loader_info = (XGL_LOADER_DATA *) pNewObject;
+ loader_info->loaderMagic = ICD_LOADER_MAGIC;
+}
+
+STATIC_INLINE bool valid_loader_magic_value(void *pNewObject) {
+ const XGL_LOADER_DATA *loader_info = (XGL_LOADER_DATA *) pNewObject;
+ return loader_info->loaderMagic == ICD_LOADER_MAGIC;
+}
+
+#endif // XGLICD_H
+
diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt
index 1570230..a4eba04 100644
--- a/loader/CMakeLists.txt
+++ b/loader/CMakeLists.txt
@@ -1,6 +1,7 @@
add_custom_command(OUTPUT dispatch.c
COMMAND ${PROJECT_SOURCE_DIR}/xgl-generate.py loader-entrypoints > dispatch.c
- DEPENDS ${PROJECT_SOURCE_DIR}/xgl-generate.py ${PROJECT_SOURCE_DIR}/xgl.py)
+ DEPENDS ${PROJECT_SOURCE_DIR}/xgl-generate.py ${PROJECT_SOURCE_DIR}/xgl.py
+ ${PROJECT_SOURCE_DIR}/include/xglIcd.h)
add_custom_command(OUTPUT table_ops.h
COMMAND ${PROJECT_SOURCE_DIR}/xgl-generate.py dispatch-table-ops loader > table_ops.h
@@ -11,6 +12,9 @@
${CMAKE_CURRENT_BINARY_DIR}
)
+# DEBUG enables runtime loader ICD verification
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
if (WIN32)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DXGL_PROTOTYPES -D_CRT_SECURE_NO_WARNINGS")
diff --git a/loader/README.md b/loader/README.md
index fe01297..f197167 100644
--- a/loader/README.md
+++ b/loader/README.md
@@ -3,9 +3,9 @@
## Overview
The Loader implements the main XGL library: libXGL.so on Linux. It handles
layer management and driver management. Loader driver management includes
-finding driver librairies and loading them. Aditionally, the loader dispatches
+finding driver librairies and loading them. Additionally, the loader dispatches
the API calls to the correct driver based on the GPU selected by the app. The
-loader fully supports multi-gpu operation.
+loader fully supports multi-gpu operation.
Loader layer management includes finding layer libraries and activating them
as requested. Loader correctly sets up layer and its own dispatch tables to
@@ -27,20 +27,22 @@
- xglCreateInstance exported
- xglDestroyInstance exported
- xglGetProcAddr exported and returns valid function pointers for all the XGL API entrypoints
-- all objects created by ICD can be cast to (XGL\_LAYER\_DISPATCH\_TABLE **)
+- all objects created by ICD can be cast to (XGL\_LAYER\_DISPATCH\_TABLE \*\*)
where the loader will replace the first entry with a pointer to the dispatch table which is
- owned by the loader. This implies two things for ICD drivers:
+ owned by the loader. This implies three things for ICD drivers:
1. the ICD must return a pointer for the opaque object handle
2. this pointer points to a regular C structure with the first entry being a pointer.
Note: for any C++ ICD's that implement XGL objects directly as C++ classes.
- The C++ compiler may put a vtable at offset zero if your class is virtual.
- In this case use a regular C structure as follows for your C++ objects:
+ The C++ compiler may put a vtable at offset zero if your class is virtual.
+ In this case use a regular C structure as follows for your C++ objects:
```
+ #include "xglIcd.h"
struct {
- void *reservedForLoader;
+ XGL_LOADER_DATA *reservedForLoader;
myObjectClass myObj;
} xglObj;
```
+ 3. the reservedForLoader.loaderMagic member must be initialized with ICD\_LOADER\_MAGIC
- the ICD may or may not implement a dispatch table
- ICD entrypoints can be named anything including the offcial xgl name such as xglCreateDevice(). However, beware of interposing by dynamic OS library loaders if the offical names are used. On Linux, if offical names are used, the ICD library must be linked with -Bsymbolic
diff --git a/loader/loader.c b/loader/loader.c
index 6dd4013..4cc7c1a 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -42,6 +42,7 @@
#include "loader_platform.h"
#include "table_ops.h"
#include "loader.h"
+#include "xglIcd.h"
struct loader_instance {
struct loader_icd *icds;
@@ -967,6 +968,14 @@
memcpy(pGpus + count, &wrapped_gpus, sizeof(*pGpus));
loader_init_dispatch_table(icd->loader_dispatch + i,
get_proc_addr, gpus[i]);
+
+ /* Verify ICD compatibility */
+ if (!valid_loader_magic_value(gpus[i])) {
+ loader_log(XGL_DBG_MSG_WARNING, 0,
+ "Loader: Incompatible ICD, first dword must be initialized to ICD_LOADER_MAGIC. See loader/README.md for details.\n");
+ assert(0);
+ }
+
const XGL_LAYER_DISPATCH_TABLE **disp;
disp = (const XGL_LAYER_DISPATCH_TABLE **) gpus[i];
*disp = icd->loader_dispatch + i;
diff --git a/xgl-generate.py b/xgl-generate.py
index 2fe51b0..202b30f 100755
--- a/xgl-generate.py
+++ b/xgl-generate.py
@@ -98,7 +98,7 @@
class LoaderEntrypointsSubcommand(Subcommand):
def generate_header(self):
- return "#include \"loader.h\""
+ return "#include \"loader.h\"\n#include \"xglIcd.h\"\n#include \"assert.h\""
def _does_function_create_object(self, proto):
out_objs = proto.object_out_params()
@@ -116,6 +116,7 @@
qual += " "
funcs = []
+ obj_check_magic_stmt = "#ifdef DEBUG\n if (!valid_loader_magic_value(*%s)) {\n assert(0 && \"Incompatible ICD, first dword must be initialized to ICD_LOADER_MAGIC. See loader/README.md for details.\");\n }\n#endif"
for proto in self.protos:
if not self._is_dispatchable(proto):
continue
@@ -124,6 +125,7 @@
decl = proto.c_func(prefix="xgl", attr="XGLAPI")
stmt = "(*disp)->%s" % proto.c_call()
if proto.name == "CreateDevice":
+ magic_stmt = obj_check_magic_stmt % proto.params[-1].name
funcs.append("%s%s\n"
"{\n"
" loader_activate_layers(%s, %s);\n"
@@ -132,28 +134,60 @@
" (const XGL_LAYER_DISPATCH_TABLE **) wrapped_obj->baseObject;\n"
" %s = wrapped_obj->nextObject;\n"
" XGL_RESULT res = %s;\n"
- " if (res == XGL_SUCCESS)\n"
+ " if (res == XGL_SUCCESS) {\n"
+ "%s\n"
" *(const XGL_LAYER_DISPATCH_TABLE **) (*%s) = *disp;\n"
+ " }\n"
" return res;\n"
"}" % (qual, decl, proto.params[0].name, proto.params[1].name,
proto.params[0].name, proto.params[0].name, stmt,
+ magic_stmt,
proto.params[-1].name))
- elif self._does_function_create_object(proto):
- if proto.name == "WsiX11CreatePresentableImage":
- obj_write_stmt = "\n *(const XGL_LAYER_DISPATCH_TABLE **) (*%s) = *disp;" % ( proto.params[-2].name)
- else:
- obj_write_stmt = ""
+ elif proto.name == "WsiX11CreatePresentableImage":
+ magic_stmt1 = obj_check_magic_stmt % proto.params[-1].name
+ magic_stmt2 = obj_check_magic_stmt % proto.params[-2].name
+ funcs.append("%s%s\n"
+ "{\n"
+ " const XGL_LAYER_DISPATCH_TABLE **disp =\n"
+ " (const XGL_LAYER_DISPATCH_TABLE **) %s;\n"
+ " XGL_RESULT res = %s;\n"
+ " if (res == XGL_SUCCESS) {\n"
+ "%s\n"
+ "%s\n"
+ " *(const XGL_LAYER_DISPATCH_TABLE **) (*%s) = *disp;\n"
+ " *(const XGL_LAYER_DISPATCH_TABLE **) (*%s) = *disp;\n"
+ " }\n"
+ " return res;\n"
+ "}"
+ % (qual, decl, proto.params[0].name, stmt, magic_stmt1, magic_stmt2, proto.params[-1].name, proto.params[-2].name))
+ elif proto.name == "GetDeviceQueue":
+ # GetDeviceQueue returns an existing Queue object so cannot check for magic header as
+ # it may have already been replaced with a function table pointer
funcs.append("%s%s\n"
"{\n"
" const XGL_LAYER_DISPATCH_TABLE **disp =\n"
" (const XGL_LAYER_DISPATCH_TABLE **) %s;\n"
" XGL_RESULT res = %s;\n"
- " if (res == XGL_SUCCESS) {%s\n"
+ " if (res == XGL_SUCCESS) {\n"
" *(const XGL_LAYER_DISPATCH_TABLE **) (*%s) = *disp;\n"
" }\n"
" return res;\n"
"}"
- % (qual, decl, proto.params[0].name, stmt, obj_write_stmt, proto.params[-1].name))
+ % (qual, decl, proto.params[0].name, stmt, proto.params[-1].name))
+ elif self._does_function_create_object(proto):
+ magic_stmt = obj_check_magic_stmt % proto.params[-1].name
+ funcs.append("%s%s\n"
+ "{\n"
+ " const XGL_LAYER_DISPATCH_TABLE **disp =\n"
+ " (const XGL_LAYER_DISPATCH_TABLE **) %s;\n"
+ " XGL_RESULT res = %s;\n"
+ " if (res == XGL_SUCCESS) {\n"
+ "%s\n"
+ " *(const XGL_LAYER_DISPATCH_TABLE **) (*%s) = *disp;\n"
+ " }\n"
+ " return res;\n"
+ "}"
+ % (qual, decl, proto.params[0].name, stmt, magic_stmt, proto.params[-1].name))
elif proto.name == "AllocDescriptorSets":
funcs.append("%s%s\n"
"{\n"
@@ -161,10 +195,16 @@
" (const XGL_LAYER_DISPATCH_TABLE **) %s;\n"
" uint32_t i;\n"
" XGL_RESULT res = %s;\n"
- " for (i = 0; i < *%s; i++)\n"
+ " for (i = 0; i < *%s; i++) {\n"
+ "#ifdef DEBUG\n"
+ " if (!valid_loader_magic_value(%s[i])) {\n"
+ " assert(0 && \"Incompatible ICD, first dword must be initialized to ICD_LOADER_MAGIC. See loader/README.md for details.\");\n"
+ " }\n"
+ "#endif\n"
" *(const XGL_LAYER_DISPATCH_TABLE **) (%s[i]) = *disp;\n"
+ " }\n"
" return res;\n"
- "}" % (qual, decl, proto.params[0].name, stmt, proto.params[-1].name, proto.params[-2].name))
+ "}" % (qual, decl, proto.params[0].name, stmt, proto.params[-1].name, proto.params[-2].name, proto.params[-2].name))
elif proto.name == "GetMultiGpuCompatibility":
funcs.append("%s%s\n"
"{\n"