blob: 44cdc9e9473600e30b7fb582593e1ed79c38d023 [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 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())