// vim: ft=cpp #include #include #include #include #include #include #include "profiler.h" #include "syscalls/helpers.h" /*[[[cog code generation from definitions.context import Context ctx = Context(definitions_path) ctx.parse("syscalls.def") syscalls = ctx.interfaces["syscalls"] for obj in syscalls.exposes: cog.outl(f'#include "objects/{obj.cname}.h"') ]]]*/ //[[[end]]] /*[[[cog code generation cog.outl(f'constexpr size_t syscall_count = {len(syscalls.methods)};') ]]]*/ //[[[end]]] DECLARE_PROFILE_CLASS(syscall_profiles, syscall_count); namespace { enum class req { required, optional, zero_ok }; template __attribute__((always_inline)) inline bool range_check(const T* param) { return reinterpret_cast(param) < arch::kernel_offset; } template __attribute__((always_inline)) inline bool check_refparam(const T* param, req required) { return range_check(param) && ((required == req::optional) || param); } template __attribute__((always_inline)) inline bool check_refparam(const T* param, size_t len, req required) { bool nullok = (required == req::optional) || (required == req::zero_ok && !len); return range_check(param) && (param || nullok); } template __attribute__((always_inline)) inline bool check_refparam(const T* param, size_t *len, req required) { if (!range_check(len)) return false; if (len) return check_refparam(param, *len, required); // if len is null but param is not, call that invalid. else return !param && required == req::optional; } // When called with more than 6 arguments, the 7th argument to the // verify function is the stack pointer holding the rest. "Pop" them // into variables to be passed normally with pop_from. template inline T pop_from(uint64_t *&sp) { return *reinterpret_cast(sp++); } } DEFINE_PROFILE_CLASS(syscall_profiles); namespace syscalls { using util::buffer; /*[[[cog code generation cbool = {True: "true", False: "false"} def get_caps(opts, type): caps = opts.get("cap", list()) return [f"j6_cap_{type.name}_{c}" for c in caps] for id, scope, method in syscalls.methods: if scope: name = f"{scope.name}_{method.name}" else: name = f"{method.name}" args = [] # The function call args to call the impl argdefs = [] # The user-facing syscall args signature cxxargdefs = [] # The kernel syscall impl args signature refparams = [] # The list of params which are pointers handles = [] objparams = [] if method.constructor: argdefs.append(("j6_handle_t *", "self")) cxxargdefs.append("j6_handle_t *self") args.append("self") refparams.append(("self", "required")) elif not method.static: argdefs.append(("j6_handle_t", "self")) if "handle" in method.options: args.append("self") else: args.append("self_obj") cxxargdefs.append(f"obj::{scope.cname} *self") objparams.append((f"obj::{scope.cname} *", "self")) handles.append(( scope.cname, "self", get_caps(method.options, scope), False)) for param in method.params: needs_obj = param.type.needs_object(param.options) needs_handle = ("handle" in param.options) or needs_obj for type, suffix in param.type.c_names(param.options): arg = f"{param.name}{suffix}" argdefs.append((type, arg)) for type, suffix in param.type.cxx_names(param.options): arg = f"{param.name}{suffix}" if needs_handle: handles.append(( param.type.object.cname, arg, get_caps(param.options, param.type.object), param.optional)) if needs_obj: objparams.append((type, arg)) args.append(f"{arg}_obj") cxxargdefs.append(f"{type} {arg}") break cxxargdefs.append(f"{type} {arg}") args.append(arg) if not needs_handle and param.caps: handles.append(( f"obj::{param.type.object.cname}", arg, get_caps(param.options, param.type.object, param.optional))) if param.refparam: subs = param.type.c_names(param.options) checkargs = map(lambda x: f"{param.name}{x[1]}", subs) refparams.append((", ".join(checkargs), param.optional)) first_args = ", ".join(map(lambda x: f"{x[0]} {x[1]}", argdefs[:6])) extra_argdefs = argdefs[6:] if extra_argdefs: first_args += ", uint64_t *sp" attr = "" if "noreturn" in method.options: attr = "[[noreturn]] " cog.outl(f"""{attr}j6_status_t {name} ({", ".join(cxxargdefs)});""") cog.outl(f"""{attr}j6_status_t _syscall_verify_{name} ({first_args}) {{""") for type, arg in extra_argdefs: cog.outl(f" {type} {arg} = pop_from<{type}>(sp);") if extra_argdefs: cog.outl() for checkargs, optional in refparams: cog.outl(f" if (!check_refparam({checkargs}, req::{optional}))") cog.outl( " return j6_err_invalid_arg;") cog.outl() if handles: cog.outl(f" j6_status_t res;") for type, arg, caps, optional in handles: capsval = "0" if caps: capsval = " | ".join(caps) optstr = (optional and "true") or "false" cog.outl(f" obj::{type} *{arg}_obj = nullptr;") cog.outl(f" res = get_handle({arg}, {capsval}, {arg}_obj, {optstr});") cog.outl(f" if (res != j6_status_ok) return res;") cog.outl() if "noreturn" in method.options: if handles: cog.outl(f""" static_assert(false, "Cannot define a `noreturn` syscall that holds handles.");""") cog.outl(f""" {name}({", ".join(args)});""") else: cog.outl(" j6_status_t _retval;") cog.outl(" {") cog.outl(f""" profiler profile {{"{name}"}};""") cog.outl(f""" _retval = {name}({", ".join(args)});""") cog.outl(" }\n") for type, arg, caps, optional in handles: cog.outl(f" release_handle({arg});") cog.outl(f""" return _retval;""") cog.outl("}") cog.outl("\n") ]]]*/ //[[[end]]] } // namespace syscalls