blob: 39d4526c48b470998c8ffcf283e9a155eb0ac1ca [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# 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.
import json
import re
import sys
def usage():
print(
"Usage:\n"
" magma_generic_cc_gen.py INPUT EXISTING OUTPUT [--debug]\n"
" INPUT json file containing the magma interface definition\n"
" EXISTING cpp file implementing zero or more magma entrypoints\n"
" OUTPUT destination path for the cpp file to generate\n"
" --debug optional flag to generate debug prints for entrypoints\n"
" Example: magma_generic_cc_gen.py magma.json magma.cc magma_generic.cc\n"
' Generates generic "glue" magma exports that directly translate between\n'
" magma commands and virtmagma structs. Does not generate implementations\n"
" for entrypoints that already exist in EXISTING."
)
# License string for the top of the file.
def license():
ret = ""
ret += "// Copyright 2019 The Fuchsia Authors. All rights reserved.\n"
ret += "// Use of this source code is governed by a BSD-style license that can be\n"
ret += "// found in the LICENSE file.\n"
return ret
# Warning string about auto-generation
def codegen_warning():
ret = ""
ret += "// NOTE: DO NOT EDIT THIS FILE! It is generated automatically by:\n"
ret += "// //src/graphics/lib/magma/src/libmagma_virt/magma_generic_cc_gen.py\n"
return ret
# Includes lists.
def includes():
ret = ""
ret += "#include <lib/magma/magma.h>\n"
ret += '#include "src/graphics/lib/magma/include/virtio/virtio_magma.h"\n'
ret += (
'#include "src/graphics/lib/magma/src/libmagma_virt/virtmagma_util.h"\n'
)
return ret
# Extract the non-"magma_" portion of the name of an export
def get_name(export):
return export["name"][len("magma_") :]
# Trim already-implemented exports from an existing file
def trim_exports(exports, file):
contents = file.readlines()
exports_out = []
for export in exports:
found = False
pattern = r".*\s+" + export["name"] + r"\s*\("
for line in contents:
if re.match(pattern, line[:-1]):
found = True
break
if not found:
exports_out += [export]
return exports_out
# Generate the signature for an export
def generate_signature(export):
ret = export["type"] + " " + export["name"] + "(\n"
for argument in export["arguments"]:
ret += " " + argument["type"] + " " + argument["name"] + ",\n"
ret = ret[:-2] + ")\n"
return ret
# Provide the appropriate error return statement for an export
def error_return(export):
if export["type"] == "magma_status_t":
return "return MAGMA_STATUS_INTERNAL_ERROR"
if export["type"] == "void":
return "return"
return "return -1"
def is_response_argument(argument):
if argument["name"][-4:] == "_out":
assert argument["type"][-1] == "*", "output argument not a pointer"
# Response arguments must be pointers to 8 byte arguments, so we can pass
# the dereferenced value over the wire in only 8 bytes.
if argument["type"].find("magma_image_info_t") != -1:
return False
return True
return False
# Splits the arguments for an export into inputs and outputs
def split_arguments(export):
inputs = []
outputs = []
for argument in export["arguments"]:
if is_response_argument(argument):
outputs += [argument]
else:
inputs += [argument]
return inputs, outputs
# Generate code to copy export arguments into the ioctl request struct
def generate_copy_in(inputs):
ret = ""
for argument in inputs:
name = argument["name"]
type = argument["type"]
if type.find("*") != -1:
ret += " request." + name + " = (uintptr_t)" + name + ";\n"
else:
ret += " request." + name + " = " + name + ";\n"
return ret
# Generate code to copy ioctl response members into export output arguments
def generate_copy_out(outputs, returns):
ret = ""
for argument in outputs:
name = argument["name"]
ret += (
" *"
+ name
+ " = (__typeof(*"
+ name
+ "))response."
+ name
+ ";\n"
)
if returns != "void":
ret += (
" "
+ returns
+ " result_return = (__typeof(result_return))(response.result_return);\n"
)
return ret
# Generate code to unwrap applicable input objects.
# Wrapped objects contain the file descriptor of the device imported via magma_device_import, since
# connections are created from devices, and buffers and semaphores are created from connections.
# Handles are not wrapped because a handle (file descriptor) may be passed into the process.
# Interfaces which can't extract a file descriptor from a wrapped parameter must have a manual
# implementation that gets the fd from elsewhere.
# Returns the name of the last wrapped parameter.
def generate_unwrap(export, needs_connection):
ret = ""
have_fd = False
for argument in export["arguments"]:
type = argument["type"]
name = argument["name"]
last_wrapped_out = name + "_wrapped"
if needs_connection and type == "magma_connection_t":
ret += " auto _connection = " + name + ";\n"
if type == "magma_connection_t" or type == "magma_device_t":
ret += (
" auto "
+ last_wrapped_out
+ " = virt"
+ type
+ "::Get("
+ name
+ ");\n"
)
ret += " " + name + " = " + last_wrapped_out + "->Object();\n"
if not have_fd:
ret += (
" int32_t file_descriptor = "
+ last_wrapped_out
+ "->Parent().fd();\n"
)
have_fd = True
if (
type == "magma_buffer_t"
or type == "magma_semaphore_t"
or type == "magma_perf_count_pool_t"
):
ret += (
" auto "
+ last_wrapped_out
+ " = virt"
+ type
+ "::Get("
+ name
+ ");\n"
)
ret += " " + name + " = " + last_wrapped_out + "->Object();\n"
if not have_fd:
ret += (
" auto _"
+ name
+ "_parent_wrapped = virtmagma_connection_t::Get("
+ last_wrapped_out
+ "->Parent());\n"
)
ret += (
" int32_t file_descriptor = _"
+ name
+ "_parent_wrapped->Parent().fd();\n"
)
have_fd = True
if type == "magma_handle_t":
# Necessary for magma_device_import, but may be incorrect for other interfaces.
if not have_fd:
ret += " int32_t file_descriptor = " + name + ";\n"
have_fd = True
if not have_fd:
sys.exit(
'error: could not retrieve virtio fd from export "'
+ export["name"]
+ '"'
)
return ret, last_wrapped_out
# Generate code to wrap applicable output objects
def generate_wrap(export):
ret = ""
needs_connection = False
for argument in export["arguments"]:
type = argument["type"]
name = argument["name"]
if type == "magma_connection_t*":
ret += (
" *"
+ name
+ " = virtmagma_connection_t::Create(*"
+ name
+ ", dup(file_descriptor))->Wrap();\n"
)
if (
type == "magma_buffer_t*"
or type == "magma_semaphore_t*"
or type == "magma_perf_count_pool_t*"
):
ret += (
" *"
+ name
+ " = virt"
+ type[:-1]
+ "::Create(*"
+ name
+ ", _connection)->Wrap();\n"
)
needs_connection = True
if type == "magma_device_t*":
ret += (
" *"
+ name
+ " = virtmagma_device_t::Create(*"
+ name
+ ", file_descriptor)->Wrap();\n"
)
return ret, needs_connection
# Generate an implementation for an export
def generate_export(export, gen_debug_prints):
name = get_name(export)
inputs, outputs = split_arguments(export)
err = error_return(export)
ret = generate_signature(export)
ret += "{\n"
if gen_debug_prints:
ret += ' printf("%s\\n", __PRETTY_FUNCTION__);\n'
for argument in export["arguments"]:
ret += (
' printf("'
+ argument["name"]
+ ' = %ld\\n", (uint64_t)'
+ argument["name"]
+ ");\n"
)
wrap_code, needs_connection = generate_wrap(export)
unwrap_code, last_wrapped_parameter = generate_unwrap(
export, needs_connection
)
ret += unwrap_code
ret += " virtio_magma_" + name + "_ctrl_t request{};\n"
ret += " virtio_magma_" + name + "_resp_t response{};\n"
ret += " request.hdr.type = VIRTIO_MAGMA_CMD_" + name.upper() + ";\n"
ret += generate_copy_in(inputs)
ret += " bool success = virtmagma_send_command(file_descriptor, &request, sizeof(request), &response, sizeof(response));\n"
if ("release" in name) and (name != "connection_release_context"):
ret += " delete " + last_wrapped_parameter + ";\n"
ret += " if (!success)\n"
ret += " " + err + ";\n"
ret += (
" if (response.hdr.type != VIRTIO_MAGMA_RESP_" + name.upper() + ")\n"
)
ret += " " + err + ";\n"
ret += generate_copy_out(outputs, export["type"])
ret += wrap_code
if export["type"] != "void":
ret += " return result_return;\n"
ret += "}\n"
return ret
def main():
nargs = len(sys.argv)
debug = False
if nargs < 4 or nargs > 5:
usage()
return 2
if nargs == 5:
if sys.argv[4] != "--debug":
usage()
return 2
debug = True
with open(sys.argv[1], "r") as file:
with open(sys.argv[2], "r") as existing:
with open(sys.argv[3], "w") as dest:
magma = json.load(file)["magma-interface"]
exports = trim_exports(magma["exports"], existing)
contents = license() + "\n"
contents += codegen_warning() + "\n"
contents += includes() + "\n"
for export in exports:
contents += generate_export(export, debug) + "\n"
dest.write(contents)
if __name__ == "__main__":
sys.exit(main())