blob: b89d9872798b99c3a7e82e761c1f6ba0c3d43634 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-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:\n"
" virtio_magma_h_gen.py FORMAT INPUT OUTPUT\n"
' FORMAT either "fuchsia" or "linux"\n'
" INPUT json file containing the magma interface definition\n"
" OUTPUT destination path for the virtio header file to generate\n"
" Example: ./virtio_magma_h_gen.py fuchsia ../magma/magma.json ./virtio_magma.h\n"
" Generates the virtio magma header based on a provided json definition,\n"
" for either fuchsia or the linux kernel."
)
# Generates a c or cpp style comment
def comment(lines, cpp):
ret = [f"// {lines[0]}\n" if cpp else f"/* {lines[0]}\n"]
if cpp:
ret.extend(f"// {line}\n" for line in lines[1:])
else:
ret.extend(f" {line}\n" for line in lines[1:])
ret.append(" */\n")
return "".join(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)
def wire_width(type):
# Default to 8 bytes
width = 8
if type == "uint64_t":
width = 8
if type == "uint32_t":
width = 4
if type == "int32_t":
width = 4
if type == "magma_bool_t":
width = 1
if type == "magma_handle_t":
width = 4
if type.find("magma_image_info_t") != -1:
width = 0 # Unknown
if type.find("magma_buffer_info_t") != -1:
width = 0 # Unknown
return width
# Wire format for a given type
def wire_format(type):
if type.find("*") != -1:
return wire_format_from_width(8)
return wire_format_from_width(wire_width(type))
# 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:",
" //src/graphics/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 = "SRC_GRAPHICS_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():
if fuchsia:
return "#include <stdint.h>\n" "#include <zircon/compiler.h>\n"
else:
return (
"#include <linux/virtio_ids.h>\n"
"#include <linux/virtio_config.h>\n"
)
# Extract the non-"magma_" portion of the name of an export
def get_name(export):
return export["name"][len("magma_") :]
# Generate a 4-digit hex string for a given integer, checking against collisions
def format_id(id, used):
ret = "0x{:04X}".format(id)
if id > len(used) or used[id]:
raise Exception("Command ID collision: " + ret)
used[id] = True
return ret
# 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"
expected_response_table = "inline enum virtio_magma_ctrl_type virtio_magma_expected_response_type(enum virtio_magma_ctrl_type type) {\n"
expected_response_table += tab + "switch (type) {\n"
command_id_base = 0x1000
response_id_base = 0x2000
error_id_base = 0x3000
max_id_count = 0x4000
used = [False] * max_id_count
for method in magma["exports"] + magma["virtmagma-internal"]:
name = get_name(method).upper()
ordinal = method["ordinal"]
assert ordinal < magma["next-free-ordinal"]
command_id = command_id_base + ordinal
response_id = response_id_base + ordinal
commands += (
tab
+ "VIRTIO_MAGMA_CMD_"
+ name
+ " = "
+ format_id(command_id, used)
+ ",\n"
)
responses += (
tab
+ "VIRTIO_MAGMA_RESP_"
+ name
+ " = "
+ format_id(response_id, used)
+ ",\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'
)
expected_response_table += (
tab
+ tab
+ "case VIRTIO_MAGMA_CMD_"
+ 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",
]
error_id = error_id_base + 1
for error_name in error_names:
errors += tab + error_name + " = " + format_id(error_id, used) + ",\n"
string_table += (
tab
+ tab
+ "case "
+ error_name
+ ': return "'
+ error_name
+ '";\n'
)
error_id = error_id + 1
string_table += (
tab + tab + 'default: return "[invalid virtio_magma_ctrl_type]";\n'
)
string_table += tab + "}\n"
string_table += "}\n"
expected_response_table += (
tab + tab + "default: return VIRTIO_MAGMA_RESP_ERR_INVALID_COMMAND;\n"
)
expected_response_table += tab + "}\n"
expected_response_table += "}\n"
ret = "enum virtio_magma_ctrl_type {\n"
ret += commands
ret += responses
ret += errors
if fuchsia:
ret += "} __PACKED;\n\n"
else:
ret += "} __attribute((packed));\n\n"
ret += string_table + "\n"
ret += expected_response_table
return ret
def is_request_argument(argument):
# Out params with unknown sizes are kept in the request struct
# because response struct elements must be 8 bytes.
if argument["name"].find("_out") == -1 or wire_width(argument["type"]) == 0:
return True
return False
# 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 = ""
ret += "typedef "
ret += "struct " + name + " {\n"
ret += tab + "virtio_magma_ctrl_hdr_t hdr;\n"
for argument in export["arguments"]:
# Include this argument iff out and resp or !out and ctrl
use = False
if is_request_argument(argument):
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)) " + name + "_t;\n"
return ret
def config_type():
global fuchsia
global tab
ret = ""
ret += "typedef "
ret += "struct virtio_magma_config {\n"
ret += tab + wire_format("uint64_t") + " dummy;\n"
if fuchsia:
ret += "} __PACKED virtio_magma_config_t;\n"
else:
ret += "} __attribute((packed)) virtio_magma_config_t;\n"
return ret
# Common control header struct
def ctrl_hdr():
global fuchsia
global tab
ret = ""
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)) virtio_magma_ctrl_hdr_t;\n"
return ret
fuchsia = True
tab = " "
def main():
global fuchsia
global tab
if len(sys.argv) != 4:
usage()
return 2
if sys.argv[1] == "linux":
fuchsia = False
tab = "\t"
elif sys.argv[1] != "fuchsia":
usage()
return 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 += config_type() + "\n"
header += gen_enums(magma) + "\n"
header += ctrl_hdr() + "\n"
for method in magma["exports"] + magma["virtmagma-internal"]:
header += format_struct(method, True) + "\n"
header += format_struct(method, False) + "\n"
if fuchsia:
header += "__END_CDECLS\n\n"
header += guards(False)
dest.write(header)
if __name__ == "__main__":
sys.exit(main())