blob: df985ea2ed523a88829ab958d164b61dbcd921f1 [file] [log] [blame]
#!/usr/bin/env python2.7
# 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:'
print ' magma_generic.cc.gen.py INPUT EXISTING OUTPUT [--debug]'
print ' INPUT json file containing the magma interface definition'
print ' EXISTING cpp file implementing zero or more magma entrypoints'
print ' OUTPUT destination path for the cpp file to generate'
print ' --debug optional flag to generate debug prints for entrypoints'
print ' Example: magma_generic.cc.gen.py magma.json magma.cc magma_generic.cc'
print ' Generates generic "glue" magma exports that directly translate between'
print ' magma commands and virtmagma structs. Does not generate implementations'
print ' 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_linux/magma_generic.cc.gen.py\n'
return ret
# Includes lists.
def includes():
ret = ''
ret += '#include "magma.h"\n'
ret += '#include "src/graphics/lib/magma/include/virtio/virtio_magma.h"\n'
ret += '#include "src/graphics/lib/magma/src/libmagma_linux/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'
# Splits the arguments for an export into inputs and outputs
def split_arguments(export):
inputs = []
outputs = []
for argument in export['arguments']:
if argument['name'][-4:] == '_out':
assert argument['type'][-1] == '*', 'output argument not a pointer'
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']
ret += ' request.' + name + ' = (__typeof(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
def generate_unwrap(export, needs_connection):
ret = ''
have_fd = False
for argument in export['arguments']:
type = argument['type']
name = argument['name']
if name == 'file_descriptor':
have_fd = True
if type == 'magma_connection_t':
if needs_connection:
ret += ' auto _connection = ' + name + ';\n'
ret += ' auto _' + name + '_wrapped = virtmagma_connection_t::Get(' + name + ');\n'
ret += ' ' + name + ' = _' + name + '_wrapped->Object();\n'
if not have_fd:
ret += ' int32_t file_descriptor = _' + name + '_wrapped->Parent().first;\n'
have_fd = True
if argument['type'] == 'magma_buffer_t':
ret += ' auto _' + name + '_wrapped = virtmagma_buffer_t::Get(' + name + ');\n'
ret += ' ' + name + ' = _' + name + '_wrapped->Object();\n'
if not have_fd:
ret += ' auto _' + name + '_parent_wrapped = virtmagma_connection_t::Get(_' + name + '_wrapped->Parent());\n'
ret += ' int32_t file_descriptor = _' + name + '_parent_wrapped->Parent().first;\n'
have_fd = True
if argument['type'] == 'magma_semaphore_t':
ret += ' auto _' + name + '_wrapped = virtmagma_semaphore_t::Get(' + name + ');\n'
ret += ' ' + name + ' = _' + name + '_wrapped->Object();\n'
if not have_fd:
ret += ' auto _' + name + '_parent_wrapped = virtmagma_connection_t::Get(_' + name + '_wrapped->Parent());\n'
ret += ' int32_t file_descriptor = _' + name + '_parent_wrapped->Parent().first;\n'
have_fd = True
if type == 'magma_device_t':
ret += ' auto _' + name + '_wrapped = virtmagma_device_t::Get(' + name + ');\n'
ret += ' ' + name + ' = _' + name + '_wrapped->Object();\n'
if not have_fd:
ret += ' int32_t file_descriptor = _' + name + '_wrapped->Parent().fd();\n'
have_fd = True
sub = 'handle'
if name[-len(sub):] == sub:
ret += ' auto _' + name + '_wrapped = GlobalHandleTable().find(' + name + ');\n'
ret += ' if (_' + name + '_wrapped == GlobalHandleTable().end())\n'
ret += ' ' + error_return(export) + ';\n'
ret += ' ' + name + ' = _' + name + '_wrapped->second->Object();\n'
if not have_fd:
ret += ' int32_t file_descriptor = _' + name + '_wrapped->second->Parent();\n'
have_fd = True
if type == 'magma_handle_t':
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
# 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 + ', virtmagma_fd_pair(file_descriptor))->Wrap();\n'
if type == 'magma_buffer_t*':
ret += ' *' + name + ' = virtmagma_buffer_t::Create(*' + name + ', _connection)->Wrap();\n'
needs_connection = True
if type == 'magma_semaphore_t*':
ret += ' *' + name + ' = virtmagma_semaphore_t::Create(*' + name + ', _connection)->Wrap();\n'
needs_connection = True
if type == 'magma_device_t*':
ret += ' *' + name + ' = virtmagma_device_t::Create(*' + name + ', file_descriptor)->Wrap();\n'
sub = 'handle_out'
if name[-len(sub):] == sub:
ret += ' GlobalHandleTable()[*' + name + '] = virtmagma_handle_t::Create(*' + name + ', file_descriptor);\n'
if export['type'] == 'magma_handle_t':
ret += ' GlobalHandleTable()[result_return] = virtmagma_handle_t::Create(result_return, file_descriptor);\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)
ret += generate_unwrap(export, needs_connection)
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 += ' if (!virtmagma_send_command(file_descriptor, &request, sizeof(request), &response, sizeof(response)))\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()
exit(-1)
if (nargs == 5):
if sys.argv[4] != '--debug':
usage()
exit(-1)
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())