blob: edbecb6c426d831b3ee98733060a5d1e9125c6f9 [file] [log] [blame]
#!/usr/bin/env 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 sys
def usage():
print 'Usage:'
print ' virtio_magma.h.gen.py FORMAT INPUT OUTPUT'
print ' FORMAT either \"fuchsia\" or \"linux\"'
print ' INPUT json file containing the magma interface definition'
print ' OUTPUT destination path for the virtio header file to generate'
print ' Example: ./virtio_magma.h.gen.py fuchsia ../magma_abi/magma.json ./virtio_magma.h'
print ' Generates the virtio magma header based on a provided json definition,'
print ' for either fuchsia or the linux kernel.'
# Generates a c or cpp style comment
def comment(lines, cpp):
ret = ('// ' if cpp else '/* ') + lines[0] + '\n'
for line in lines[1:]:
ret += ('// ' if cpp else ' ') + line + '\n'
if not cpp:
ret = ret[:-1] + ' */\n'
return ret
# Wire formats for various widths
def wire_format_from_width(width):
global fuchsia
global tab
format_fuchsia = {
1: 'uint8_t',
2: 'uint16_t',
4: 'uint32_t',
8: 'uint64_t',
}
format_linux = {
1: 'u8',
2: '__le16',
4: '__le32',
8: '__le64',
}
invalid = 'INVALID TYPE WIDTH'
if fuchsia:
return format_fuchsia.get(width, invalid)
return format_linux.get(width, invalid)
# Wire format for a given type
def wire_format(type):
# Default to 8 bytes
width = 8
if type.find('*') != -1: width = 8
if type == 'uint32_t': width = 4
if type == 'int32_t': width = 4
if type == 'magma_bool_t': width = 1
return wire_format_from_width(width)
# License string for the top of the file.
def license():
global fuchsia
lines = [
'Copyright 2018 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.'
]
return comment(lines, fuchsia)
# Warning string about auto-generation
def warning():
global fuchsia
lines = [
'NOTE: DO NOT EDIT THIS FILE! It is generated automatically by:',
' //garnet/lib/magma/include/virtio/virtio_magma.h.gen.py'
]
return comment(lines, fuchsia)
# Guard macro that goes at the beginning/end of the header (after license).
def guards(begin):
global fuchsia
global tab
macro = '_LINUX_VIRTIO_MAGMA_H'
if fuchsia:
macro = 'GARNET_LIB_MAGMA_INCLUDE_VIRTIO_VIRTIO_MAGMA_H_'
if begin:
return '#ifndef ' + macro + '\n#define ' + macro + '\n'
return '#endif ' + comment([macro], fuchsia)
# Includes lists.
def includes():
ret = ''
if fuchsia:
ret += '#include <stdint.h>\n'
ret += '#include <zircon/compiler.h>\n'
else:
ret += '#include <linux/virtio_ids.h>\n'
ret += '#include <linux/virtio_config.h>\n'
ret += '#include <linux/virtmagma.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 enum
def gen_enums(magma):
global fuchsia
global tab
commands = tab + comment(['magma commands'], fuchsia)
responses = tab + comment(['magma success responses'], fuchsia)
errors = tab + comment(['magma error responses'], fuchsia)
string_table = 'inline const char* virtio_magma_ctrl_type_string(enum virtio_magma_ctrl_type type) {\n'
string_table += tab + 'switch (type) {\n'
command_id = ' = 0x0400'
response_id = ' = 0x1180'
error_id = ' = 0x1280'
for export in magma['exports']:
name = get_name(export).upper()
commands += tab + 'VIRTIO_MAGMA_CMD_' + name + command_id + ',\n'
responses += tab + 'VIRTIO_MAGMA_RESP_' + name + response_id + ',\n'
command_id = response_id = ''
string_table += tab + tab + 'case VIRTIO_MAGMA_CMD_' + name + ': return "VIRTIO_MAGMA_CMD_' + name + '";\n'
string_table += tab + tab + 'case VIRTIO_MAGMA_RESP_' + name + ': return "VIRTIO_MAGMA_RESP_' + name + '";\n'
error_names = [
'VIRTIO_MAGMA_RESP_ERR_UNIMPLEMENTED',
'VIRTIO_MAGMA_RESP_ERR_INTERNAL',
'VIRTIO_MAGMA_RESP_ERR_HOST_DISCONNECTED',
'VIRTIO_MAGMA_RESP_ERR_OUT_OF_MEMORY',
'VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND',
'VIRTIO_MAGMA_RESP_ERR_INVALID_ARGUMENT'
]
errors += tab + error_names[0] + error_id + ',\n'
for error_name in error_names[1:]:
errors += tab + error_name + ',\n'
string_table += tab + tab + 'case ' + error_name + ': return "' + error_name + '";\n'
string_table += tab + tab + 'default: return "[invalid virtio_magma_ctrl_type]";\n'
string_table += tab + '}\n'
string_table += '}\n'
ret = 'enum virtio_magma_ctrl_type {\n'
ret += commands
ret += responses
ret += errors
if fuchsia:
ret += '} __PACKED;\n'
else:
ret += '} __attribute((packed));\n\n'
ret += string_table
return ret
# Format command or response struct for an export
def format_struct(export, ctrl):
global fuchsia
global tab
name = 'virtio_magma_' + get_name(export) + '_' + ('ctrl' if ctrl else 'resp')
ret = ''
if fuchsia:
ret += 'typedef '
ret += 'struct ' + name + ' {\n'
if fuchsia:
ret += tab + 'virtio_magma_ctrl_hdr_t hdr;\n'
else:
ret += tab + 'struct virtio_magma_ctrl_hdr hdr;\n'
for argument in export['arguments']:
# Include this argument iff out and resp or !out and ctrl
use = False
if argument['name'].find('_out') == -1:
if ctrl:
use = True
else:
if not ctrl:
use = True
if use:
ret += tab + wire_format(argument['type']) + ' ' + argument['name'] + ';\n'
# Add return value, if any
if not ctrl:
if export['type'] != 'void':
ret += tab + wire_format(export['type']) + ' result_return;\n'
if fuchsia:
ret += '} __PACKED ' + name + '_t;\n'
else:
ret += '} __attribute((packed));\n'
return ret
# Common control header struct
def ctrl_hdr():
global fuchsia
global tab
ret = ''
if fuchsia:
ret += 'typedef '
ret += 'struct virtio_magma_ctrl_hdr {\n'
ret += tab + wire_format('uint32_t') + ' type;\n'
ret += tab + wire_format('uint32_t') + ' flags;\n'
if fuchsia:
ret += '} __PACKED virtio_magma_ctrl_hdr_t;\n'
else:
ret += '} __attribute((packed));\n'
return ret
fuchsia = True
tab = ' '
def main():
global fuchsia
global tab
if (len(sys.argv) != 4):
usage()
exit(-1)
if (sys.argv[1] == 'linux'):
fuchsia = False
tab = '\t'
elif (sys.argv[1] != 'fuchsia'):
usage()
exit(-2)
with open(sys.argv[2], 'r') as file:
with open(sys.argv[3], 'w') as dest:
magma = json.load(file)['magma-interface']
header = license() + '\n'
header += warning() + '\n'
header += guards(True) + '\n'
header += includes() + '\n'
if fuchsia:
header += '__BEGIN_CDECLS\n\n'
header += gen_enums(magma) + '\n'
header += ctrl_hdr() + '\n'
for export in magma['exports']:
header += format_struct(export, True) + '\n'
header += format_struct(export, False) + '\n'
if fuchsia:
header += '__END_CDECLS\n\n'
header += guards(False)
dest.write(header)
if __name__ == '__main__':
sys.exit(main())