| #!/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' |
| 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 += ' fbl::unique_fd device_fd_;\n' |
| header += '};\n' |
| header += guards(False) + '\n' |
| dest.write(header) |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |