drivers: Perform a runtime check if a driver is capable of an operation

Driver APIs might not implement all operations, making it possible for
a user thread to get the kernel to execute a function at 0x00000000.

Perform runtime checks in all the driver handlers, checking if they're
capable of performing the requested operation.

Fixes #6907.

Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
diff --git a/scripts/gen_kobject_list.py b/scripts/gen_kobject_list.py
index fbb44ee..559b2f0 100755
--- a/scripts/gen_kobject_list.py
+++ b/scripts/gen_kobject_list.py
@@ -117,6 +117,30 @@
     fp.write(footer)
 
 
+driver_macro_tpl = """
+#define _SYSCALL_DRIVER_%(driver_upper)s(ptr, op) _SYSCALL_DRIVER_GEN(ptr, op, %(driver_lower)s, %(driver_upper)s)
+"""
+
+def write_validation_output(fp):
+    fp.write("#ifndef __DRIVER_VALIDATION_GEN_H__\n")
+    fp.write("#define __DRIVER_VALIDATION_GEN_H__\n")
+
+    fp.write("""#define _SYSCALL_DRIVER_GEN(ptr, op, driver_lower_case, driver_upper_case) \\
+	do { \\
+		_SYSCALL_OBJ(ptr, K_OBJ_DRIVER_##driver_upper_case); \\
+		_SYSCALL_DRIVER_OP(ptr, driver_lower_case##_driver_api, op); \\
+	} while (0)\n\n""");
+
+    for subsystem in subsystems:
+        subsystem = subsystem.replace("_driver_api", "")
+
+        fp.write(driver_macro_tpl % {
+            "driver_lower": subsystem.lower(),
+            "driver_upper": subsystem.upper(),
+        })
+
+    fp.write("#endif /* __DRIVER_VALIDATION_GEN_H__ */\n")
+
 def parse_args():
     global args
 
@@ -124,11 +148,14 @@
         description=__doc__,
         formatter_class=argparse.RawDescriptionHelpFormatter)
 
-    parser.add_argument("-k", "--kernel", required=True,
+    parser.add_argument("-k", "--kernel", required=False,
                         help="Input zephyr ELF binary")
     parser.add_argument(
-        "-o", "--output", required=True,
+        "-g", "--gperf-output", required=False,
         help="Output list of kernel object addresses for gperf use")
+    parser.add_argument(
+        "-V", "--validation-output", required=False,
+        help="Output driver validation macros")
     parser.add_argument("-v", "--verbose", action="store_true",
                         help="Print extra debugging information")
     args = parser.parse_args()
@@ -139,20 +166,26 @@
 def main():
     parse_args()
 
-    eh = ElfHelper(args.kernel, args.verbose, kobjects, subsystems)
-    syms = eh.get_symbols()
-    max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8
-    objs = eh.find_kobjects(syms)
+    if args.gperf_output:
+        eh = ElfHelper(args.kernel, args.verbose, kobjects, subsystems)
+        syms = eh.get_symbols()
+        max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8
+        objs = eh.find_kobjects(syms)
 
-    if eh.get_thread_counter() > max_threads:
-        sys.stderr.write("Too many thread objects (%d)\n" % thread_counter)
-        sys.stderr.write("Increase CONFIG_MAX_THREAD_BYTES to %d\n",
-                         -(-thread_counter // 8))
-        sys.exit(1)
+        if eh.get_thread_counter() > max_threads:
+            sys.stderr.write("Too many thread objects (%d)\n" % thread_counter)
+            sys.stderr.write("Increase CONFIG_MAX_THREAD_BYTES to %d\n",
+                             -(-thread_counter // 8))
+            sys.exit(1)
 
-    with open(args.output, "w") as fp:
-        write_gperf_table(fp, eh, objs, syms["_static_kernel_objects_begin"],
-                          syms["_static_kernel_objects_end"])
+        with open(args.gperf_output, "w") as fp:
+            write_gperf_table(fp, eh, objs,
+                              syms["_static_kernel_objects_begin"],
+                              syms["_static_kernel_objects_end"])
+
+    if args.validation_output:
+        with open(args.validation_output, "w") as fp:
+            write_validation_output(fp)
 
 
 if __name__ == "__main__":