blob: 3978b248beccda64bd420d9de88bf62364f82c61 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <functional>
#include "tools/kazoo/output_util.h"
#include "tools/kazoo/outputs.h"
namespace {
// Each incoming argument directly from the user is declared as using the
// widened type (always either int64_t or uint64_t) so the compiler is under no
// illusions that it can trust the incoming register values not to have excess
// high bit values set (or cleared for negative signed values). Then the
// wrapper will safely narrow the register value into the argument value of the
// declared type. See //zircon/kernel/lib/syscalls/safe-syscall-argument.h for
// the SafeSyscallArgument template class that provides the RawType type and
// the Sanitize function used in the generated code.
std::string ArgumentExpr(const StructMember& arg) {
return "SafeSyscallArgument<" + GetCUserModeName(arg.type()) + ">::Sanitize(" + arg.name() + ")";
}
} // namespace
bool KernelWrappersOutput(const SyscallLibrary& library, Writer* writer) {
CopyrightHeaderWithCppComments(writer);
writer->Puts("extern \"C\" {\n\n");
constexpr const char indent[] = " ";
for (const auto& syscall : library.syscalls()) {
if (syscall->HasAttribute("vdsocall")) {
continue;
}
auto write_prototype = [&]() {
writer->Printf("syscall_result wrapper_%s(", syscall->name().c_str());
for (const auto& arg : syscall->kernel_arguments()) {
writer->Printf("SafeSyscallArgument<%s>::RawType %s, ",
GetCUserModeName(arg.type()).c_str(), arg.name().c_str());
}
writer->Puts("uint64_t pc)");
};
write_prototype();
writer->Printf(";\n");
write_prototype();
writer->Printf(
" {\n%sreturn do_syscall(ZX_SYS_%s, pc, &VDso::ValidSyscallPC::%s, [&](ProcessDispatcher* "
"current_process) -> uint64_t {\n",
indent, syscall->name().c_str(), syscall->name().c_str());
// Write out locals for the output handles.
std::vector<std::reference_wrapper<const StructMember>> out_handle_args;
for (const auto& arg : syscall->kernel_arguments()) {
const Type& type = arg.type();
if (type.IsPointer()) {
std::string pointed_to = GetCKernelModeName(type.DataAsPointer().pointed_to_type());
if (type.constness() == Constness::kMutable && pointed_to == "zx_handle_t" &&
!type.DataAsPointer().was_vector()) {
out_handle_args.push_back(arg);
writer->Printf("%s%suser_out_handle out_handle_%s;\n", indent, indent,
arg.name().c_str());
}
}
}
writer->Printf(
"%s%s%s sys_%s(", indent, indent,
syscall->is_noreturn() ? "/*noreturn*/" : "auto result =", syscall->name().c_str());
for (size_t i = 0; i < syscall->kernel_arguments().size(); ++i) {
const StructMember& arg = syscall->kernel_arguments()[i];
const Type& type = arg.type();
const std::string arg_expr = ArgumentExpr(arg);
if (type.IsPointer()) {
std::string pointed_to = GetCKernelModeName(type.DataAsPointer().pointed_to_type());
if (type.constness() == Constness::kConst) {
writer->Printf("make_user_in_ptr(%s)", arg_expr.c_str());
} else if (type.constness() == Constness::kMutable) {
if (pointed_to == "zx_handle_t" && !type.DataAsPointer().was_vector()) {
writer->Printf("&out_handle_%s", arg.name().c_str());
} else if (type.optionality() == Optionality::kInputArgument) {
writer->Printf("make_user_inout_ptr(%s)", arg_expr.c_str());
} else {
writer->Printf("make_user_out_ptr(%s)", arg_expr.c_str());
}
}
} else {
writer->Puts(arg_expr);
}
if (i != syscall->kernel_arguments().size() - 1) {
writer->Puts(", ");
}
}
writer->Puts(");\n");
// Complete copy out of output handles.
if (!out_handle_args.empty()) {
writer->Printf("%s%sif (result != ZX_OK)\n", indent, indent);
writer->Printf("%s%s%sreturn result;\n", indent, indent, indent);
for (const StructMember& arg : out_handle_args) {
writer->Printf(
"%s%sresult = out_handle_%s.begin_copyout(current_process, make_user_out_ptr(%s));\n",
indent, indent, arg.name().c_str(), ArgumentExpr(arg).c_str());
writer->Printf("%s%sif (result != ZX_OK)\n", indent, indent);
writer->Printf("%s%s%sreturn result;\n", indent, indent, indent);
}
for (const StructMember& arg : out_handle_args) {
writer->Printf("%s%sout_handle_%s.finish_copyout(current_process);\n", indent, indent,
arg.name().c_str());
}
}
if (syscall->is_noreturn()) {
writer->Printf("%s%s/* NOTREACHED */\n", indent, indent);
writer->Printf("%s%sreturn ZX_ERR_BAD_STATE;\n", indent, indent);
} else {
writer->Printf("%s%sreturn result;\n", indent, indent);
}
writer->Printf("%s});\n", indent);
writer->Puts("}\n\n");
}
writer->Puts("}\n");
return true;
}