<reponame>sunshinewithmoonlight/fuchsia-2003
#!/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 sys


def usage():
    print 'Usage:'
    print '  virtio_magma_generic.h.gen.py INPUT OUTPUT'
    print '    INPUT    json file containing the magma interface definition'
    print '    OUTPUT   destination path for the header file to generate'
    print '  Example: virtio_magma_generic.h.gen.py magma.json virtio_magma_generic.h'
    print '  Generates a generic "glue" class that directly translates between'
    print '  virtmagma structs and magma commands, that may be overridden.'


# 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 warning():
    ret = ''
    ret += '// NOTE: DO NOT EDIT THIS FILE! It is generated automatically by:\n'
    ret += '//   //src/virtualization/bin/vmm/device/virtio_magma_generic.h.gen.py\n'
    return ret


# Guard macro that goes at the beginning/end of the header (after license).
def guards(begin):
    macro = 'SRC_VIRTUALIZATION_BIN_VMM_DEVICE_VIRTIO_MAGMA_GENERIC_H_'
    if begin:
        return '#ifndef ' + macro + '\n#define ' + macro + '\n'
    return '#endif // ' + macro


# Includes lists.
def includes():
    ret = ''
    ret += '#include <fbl/unique_fd.h>\n'
    ret += '#include "src/lib/fxl/macros.h"\n'
    ret += '#include <trace/event.h>\n'
    ret += '#include <zircon/status.h>\n'
    ret += '#include "src/virtualization/bin/vmm/device/virtio_queue.h"\n'
    ret += '#include <string>\n'
    return ret


# Extract the non-"magma_" portion of the name of an export
def get_name(export):
    return export['name'][len('magma_'):]


# Generate a method that does simple validation of a virtio command struct,
# passes it on to magma, and populates the corresponding response struct.
def generate_generic_method(export):
    name = get_name(export)
    ret = ''
    ret += '  virtual zx_status_t Handle_' + name + '(\n'
    ret += '    const virtio_magma_' + name + '_ctrl_t* request,\n'
    ret += '    virtio_magma_' + name + '_resp_t* response) {\n'
    ret += '    TRACE_DURATION("machina", "VirtioMagmaGeneric::Handle_' + name + '");\n'
    ret += '    FXL_DCHECK(request->hdr.type == VIRTIO_MAGMA_CMD_' + name.upper(
    ) + ');\n'
    invocation_args = ''
    copy_temporaries = ''
    for argument in export['arguments']:
        invocation_args += '      '
        if argument['name'].find('_out') != -1:
            assert (
                argument['type'][-1] == '*'), 'output argument not a pointer'
            # Create locals for output parameters. This is necessary due to struct packing.
            temp_name = 'temp_' + argument['name']
            ret += '    ' + argument['type'][:-1] + ' ' + temp_name + '{};\n'
            invocation_args += '&' + temp_name + ',\n'
            copy_temporaries += '    response->' + argument[
                'name'] + ' = (__typeof(response->' + argument[
                    'name'] + '))' + temp_name + ';\n'
        else:
            invocation_args += '(' + argument['type'] + ')request->' + argument[
                'name'] + ',\n'

    ret += '    '
    if export['type'] != 'void':
        ret += 'response->result_return = '
    ret += export['name'] + '(\n'
    ret += invocation_args[:-2] + ');\n'
    ret += copy_temporaries
    ret += '    response->hdr.type = VIRTIO_MAGMA_RESP_' + name.upper() + ';\n'
    ret += '    return ZX_OK;\n'
    ret += '  }\n'
    return ret


# Generate the main command switch method
def generate_handle_command(magma):
    ret = '''  zx_status_t HandleCommand(VirtioChain chain) {
    TRACE_DURATION("machina", "VirtioMagma::HandleCommand");
    VirtioDescriptor request_desc{};
    if (!chain.NextDescriptor(&request_desc)) {
      FXL_LOG(ERROR) << "Failed to read request descriptor";
      return ZX_ERR_INTERNAL;
    }
    const auto request_header = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(request_desc.addr);
    const uint32_t command_type = request_header->type;
    if (!chain.HasDescriptor()) {
      FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") missing response descriptor";
      return ZX_ERR_INVALID_ARGS;
    }
    VirtioDescriptor response_desc{};
    if (!chain.NextDescriptor(&response_desc)) {
      FXL_LOG(ERROR) << "Failed to read response descriptor";
      return ZX_ERR_INTERNAL;
    }
    if (!response_desc.writable) {
      FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") response descriptor not writable";
      return ZX_ERR_INVALID_ARGS;
    }
    if (!device_fd_.is_valid()) {
      auto response_header = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(response_desc.addr);
      response_header->type = VIRTIO_MAGMA_RESP_ERR_HOST_DISCONNECTED;
      chain.Return();
      return ZX_OK;
    }
    switch (command_type) {
'''
    for export in magma['exports']:
        name = get_name(export)
        ret += '      case VIRTIO_MAGMA_CMD_' + name.upper() + ': {\n'
        ret += '        auto request = reinterpret_cast<const virtio_magma_' + name + '_ctrl_t*>(request_desc.addr);\n'
        ret += '        auto response = reinterpret_cast<virtio_magma_' + name + '_resp_t*>(response_desc.addr);\n'
        ret += '#ifdef VIRTMAGMA_DEBUG\n'
        ret += '        FXL_LOG(INFO) << "Received MAGMA command (" << command_type << "):\\n"\\\n'
        ret += '          "  hdr = { " << virtio_magma_ctrl_type_string((virtio_magma_ctrl_type)request->hdr.type) << ", " << request->hdr.flags << " }"\\\n'
        for argument in export['arguments']:
            if argument['name'].find('_out') == -1:
                ret += '          "\\n  ' + argument[
                    'name'] + ' = " << static_cast<uint64_t>(request->' + argument[
                        'name'] + ') << ""\\\n'
        ret += '          "";\n'
        ret += '#endif // VIRTMAGMA_DEBUG\n'
        ret += '        if (response_desc.len < sizeof(*response)) {\n'
        ret += '          FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") response descriptor too small";\n'
        ret += '          chain.Return();\n'
        ret += '          return ZX_ERR_INVALID_ARGS;\n'
        ret += '        }\n'
        ret += '        zx_status_t status = Handle_' + name + '(request, response);\n'
        ret += '        if (status != ZX_OK) {\n'
        ret += '          FXL_LOG(ERROR) << "Handle_' + name + ' failed (" << zx_status_get_string(status) << ")";\n'
        ret += '          chain.Return();\n'
        ret += '          return status;\n'
        ret += '        }\n'
        ret += '#ifdef VIRTMAGMA_DEBUG\n'
        ret += '        FXL_LOG(INFO) << "Sending MAGMA response:\\n"\\\n'
        ret += '          "  hdr = { " << virtio_magma_ctrl_type_string((virtio_magma_ctrl_type)response->hdr.type) << ", " << response->hdr.flags << " }"\\\n'
        for argument in export['arguments']:
            if argument['name'].find('_out') != -1:
                ret += '          "\\n  ' + argument[
                    'name'] + ' = " << static_cast<uint64_t>(response->' + argument[
                        'name'] + ') << ""\\\n'
        if export['type'] != 'void':
            ret += '          "\\n  result_return = " << static_cast<uint64_t>(response->result_return) << ""\\\n'
        ret += '          "";\n'
        ret += '#endif // VIRTMAGMA_DEBUG\n'
        ret += '        *chain.Used() = sizeof(*response);\n'
        ret += '      } break;\n'
    ret += '''      default: {
        FXL_LOG(ERROR) << "Unsupported MAGMA command (" << command_type << ")";
        auto response = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(response_desc.addr);
        response->type = VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND;
        *chain.Used() = sizeof(*response);
      } break;
    }
    chain.Return();
    return ZX_OK;
  }'''
    return ret


def main():
    if (len(sys.argv) != 3):
        usage()
        exit(-1)
    with open(sys.argv[1], 'r') as file:
        with open(sys.argv[2], 'w') as dest:
            magma = json.load(file)['magma-interface']
            header = license() + '\n'
            header += warning() + '\n'
            header += guards(True) + '\n'
            header += includes() + '\n'
            header += 'class VirtioMagmaGeneric {\n'
            header += ' public:\n'
            for export in magma['exports']:
                header += generate_generic_method(export) + '\n'
            header += generate_handle_command(magma) + '\n'
            header += ' protected:\n'
            header += '  std::string device_path_;\n'
            header += '  fbl::unique_fd device_fd_;\n'
            header += '};\n'
            header += guards(False) + '\n'
            dest.write(header)


if __name__ == '__main__':
    sys.exit(main())

#!/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 sys


def usage():
    print 'Usage:'
    print '  virtio_magma_generic.h.gen.py INPUT OUTPUT'
    print '    INPUT    json file containing the magma interface definition'
    print '    OUTPUT   destination path for the header file to generate'
    print '  Example: virtio_magma_generic.h.gen.py magma.json virtio_magma_generic.h'
    print '  Generates a generic "glue" class that directly translates between'
    print '  virtmagma structs and magma commands, that may be overridden.'


# 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 warning():
    ret = ''
    ret += '// NOTE: DO NOT EDIT THIS FILE! It is generated automatically by:\n'
    ret += '//   //src/virtualization/bin/vmm/device/virtio_magma_generic.h.gen.py\n'
    return ret


# Guard macro that goes at the beginning/end of the header (after license).
def guards(begin):
    macro = 'SRC_VIRTUALIZATION_BIN_VMM_DEVICE_VIRTIO_MAGMA_GENERIC_H_'
    if begin:
        return '#ifndef ' + macro + '\n#define ' + macro + '\n'
    return '#endif // ' + macro


# Includes lists.
def includes():
    ret = ''
    ret += '#include <fbl/unique_fd.h>\n'
    ret += '#include "src/lib/fxl/macros.h"\n'
    ret += '#include <trace/event.h>\n'
    ret += '#include <zircon/status.h>\n'
    ret += '#include "src/virtualization/bin/vmm/device/virtio_queue.h"\n'
    ret += '#include <string>\n'
    return ret


# Extract the non-"magma_" portion of the name of an export
def get_name(export):
    return export['name'][len('magma_'):]


# Generate a method that does simple validation of a virtio command struct,
# passes it on to magma, and populates the corresponding response struct.
def generate_generic_method(export):
    name = get_name(export)
    ret = ''
    ret += '  virtual zx_status_t Handle_' + name + '(\n'
    ret += '    const virtio_magma_' + name + '_ctrl_t* request,\n'
    ret += '    virtio_magma_' + name + '_resp_t* response) {\n'
    ret += '    TRACE_DURATION("machina", "VirtioMagmaGeneric::Handle_' + name + '");\n'
    ret += '    FXL_DCHECK(request->hdr.type == VIRTIO_MAGMA_CMD_' + name.upper(
    ) + ');\n'
    invocation_args = ''
    copy_temporaries = ''
    for argument in export['arguments']:
        invocation_args += '      '
        if argument['name'].find('_out') != -1:
            assert (
                argument['type'][-1] == '*'), 'output argument not a pointer'
            # Create locals for output parameters. This is necessary due to struct packing.
            temp_name = 'temp_' + argument['name']
            ret += '    ' + argument['type'][:-1] + ' ' + temp_name + '{};\n'
            invocation_args += '&' + temp_name + ',\n'
            copy_temporaries += '    response->' + argument[
                'name'] + ' = (__typeof(response->' + argument[
                    'name'] + '))' + temp_name + ';\n'
        else:
            invocation_args += '(' + argument['type'] + ')request->' + argument[
                'name'] + ',\n'

    ret += '    '
    if export['type'] != 'void':
        ret += 'response->result_return = '
    ret += export['name'] + '(\n'
    ret += invocation_args[:-2] + ');\n'
    ret += copy_temporaries
    ret += '    response->hdr.type = VIRTIO_MAGMA_RESP_' + name.upper() + ';\n'
    ret += '    return ZX_OK;\n'
    ret += '  }\n'
    return ret


# Generate the main command switch method
def generate_handle_command(magma):
    ret = '''  zx_status_t HandleCommand(VirtioChain chain) {
    TRACE_DURATION("machina", "VirtioMagma::HandleCommand");
    VirtioDescriptor request_desc{};
    if (!chain.NextDescriptor(&request_desc)) {
      FXL_LOG(ERROR) << "Failed to read request descriptor";
      return ZX_ERR_INTERNAL;
    }
    const auto request_header = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(request_desc.addr);
    const uint32_t command_type = request_header->type;
    if (!chain.HasDescriptor()) {
      FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") missing response descriptor";
      return ZX_ERR_INVALID_ARGS;
    }
    VirtioDescriptor response_desc{};
    if (!chain.NextDescriptor(&response_desc)) {
      FXL_LOG(ERROR) << "Failed to read response descriptor";
      return ZX_ERR_INTERNAL;
    }
    if (!response_desc.writable) {
      FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") response descriptor not writable";
      return ZX_ERR_INVALID_ARGS;
    }
    if (!device_fd_.is_valid()) {
      auto response_header = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(response_desc.addr);
      response_header->type = VIRTIO_MAGMA_RESP_ERR_HOST_DISCONNECTED;
      chain.Return();
      return ZX_OK;
    }
    switch (command_type) {
'''
    for export in magma['exports']:
        name = get_name(export)
        ret += '      case VIRTIO_MAGMA_CMD_' + name.upper() + ': {\n'
        ret += '        auto request = reinterpret_cast<const virtio_magma_' + name + '_ctrl_t*>(request_desc.addr);\n'
        ret += '        auto response = reinterpret_cast<virtio_magma_' + name + '_resp_t*>(response_desc.addr);\n'
        ret += '#ifdef VIRTMAGMA_DEBUG\n'
        ret += '        FXL_LOG(INFO) << "Received MAGMA command (" << command_type << "):\\n"\\\n'
        ret += '          "  hdr = { " << virtio_magma_ctrl_type_string((virtio_magma_ctrl_type)request->hdr.type) << ", " << request->hdr.flags << " }"\\\n'
        for argument in export['arguments']:
            if argument['name'].find('_out') == -1:
                ret += '          "\\n  ' + argument[
                    'name'] + ' = " << static_cast<uint64_t>(request->' + argument[
                        'name'] + ') << ""\\\n'
        ret += '          "";\n'
        ret += '#endif // VIRTMAGMA_DEBUG\n'
        ret += '        if (response_desc.len < sizeof(*response)) {\n'
        ret += '          FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") response descriptor too small";\n'
        ret += '          chain.Return();\n'
        ret += '          return ZX_ERR_INVALID_ARGS;\n'
        ret += '        }\n'
        ret += '        zx_status_t status = Handle_' + name + '(request, response);\n'
        ret += '        if (status != ZX_OK) {\n'
        ret += '          FXL_LOG(ERROR) << "Handle_' + name + ' failed (" << zx_status_get_string(status) << ")";\n'
        ret += '          chain.Return();\n'
        ret += '          return status;\n'
        ret += '        }\n'
        ret += '#ifdef VIRTMAGMA_DEBUG\n'
        ret += '        FXL_LOG(INFO) << "Sending MAGMA response:\\n"\\\n'
        ret += '          "  hdr = { " << virtio_magma_ctrl_type_string((virtio_magma_ctrl_type)response->hdr.type) << ", " << response->hdr.flags << " }"\\\n'
        for argument in export['arguments']:
            if argument['name'].find('_out') != -1:
                ret += '          "\\n  ' + argument[
                    'name'] + ' = " << static_cast<uint64_t>(response->' + argument[
                        'name'] + ') << ""\\\n'
        if export['type'] != 'void':
            ret += '          "\\n  result_return = " << static_cast<uint64_t>(response->result_return) << ""\\\n'
        ret += '          "";\n'
        ret += '#endif // VIRTMAGMA_DEBUG\n'
        ret += '        *chain.Used() = sizeof(*response);\n'
        ret += '      } break;\n'
    ret += '''      default: {
        FXL_LOG(ERROR) << "Unsupported MAGMA command (" << command_type << ")";
        auto response = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(response_desc.addr);
        response->type = VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND;
        *chain.Used() = sizeof(*response);
      } break;
    }
    chain.Return();
    return ZX_OK;
  }'''
    return ret


def main():
    if (len(sys.argv) != 3):
        usage()
        exit(-1)
    with open(sys.argv[1], 'r') as file:
        with open(sys.argv[2], 'w') as dest:
            magma = json.load(file)['magma-interface']
            header = license() + '\n'
            header += warning() + '\n'
            header += guards(True) + '\n'
            header += includes() + '\n'
            header += 'class VirtioMagmaGeneric {\n'
            header += ' public:\n'
            for export in magma['exports']:
                header += generate_generic_method(export) + '\n'
            header += generate_handle_command(magma) + '\n'
            header += ' protected:\n'
            header += '  std::string device_path_;\n'
            header += '  fbl::unique_fd device_fd_;\n'
            header += '};\n'
            header += guards(False) + '\n'
            dest.write(header)


if __name__ == '__main__':
    sys.exit(main())
#!/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. # License string for the top of the file. # Warning string about auto-generation # Guard macro that goes at the beginning/end of the header (after license). #define ' + macro + '\n' # Includes lists. # Extract the non-"magma_" portion of the name of an export # Generate a method that does simple validation of a virtio command struct, # passes it on to magma, and populates the corresponding response struct. # Create locals for output parameters. This is necessary due to struct packing. # Generate the main command switch method zx_status_t HandleCommand(VirtioChain chain) {     TRACE_DURATION("machina", "VirtioMagma::HandleCommand");     VirtioDescriptor request_desc{};     if (!chain.NextDescriptor(&request_desc)) {       FXL_LOG(ERROR) << "Failed to read request descriptor";       return ZX_ERR_INTERNAL;     }     const auto request_header = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(request_desc.addr);     const uint32_t command_type = request_header->type;     if (!chain.HasDescriptor()) {       FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") missing response descriptor";       return ZX_ERR_INVALID_ARGS;     }     VirtioDescriptor response_desc{};     if (!chain.NextDescriptor(&response_desc)) {       FXL_LOG(ERROR) << "Failed to read response descriptor";       return ZX_ERR_INTERNAL;     }     if (!response_desc.writable) {       FXL_LOG(ERROR) << "MAGMA command (" << command_type << ") response descriptor not writable";       return ZX_ERR_INVALID_ARGS;     }     if (!device_fd_.is_valid()) {       auto response_header = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(response_desc.addr);       response_header->type = VIRTIO_MAGMA_RESP_ERR_HOST_DISCONNECTED;       chain.Return();       return ZX_OK;     }     switch (command_type) { default: {         FXL_LOG(ERROR) << "Unsupported MAGMA command (" << command_type << ")";         auto response = reinterpret_cast<virtio_magma_ctrl_hdr_t*>(response_desc.addr);         response->type = VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND;         *chain.Used() = sizeof(*response);       } break;     }     chain.Return();     return ZX_OK;   }