mirror of
https://github.com/hermitcore/libhermit.git
synced 2025-03-23 00:00:05 +01:00
420 lines
16 KiB
Python
Executable file
420 lines
16 KiB
Python
Executable file
#!/usr/bin/env python
|
|
"""Copyright (c) 2018, Annika Wierichs, RWTH Aachen University
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the University nor the names of its contributors
|
|
may be used to endorse or promote products derived from this
|
|
software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
"""
|
|
|
|
import custom_snippets
|
|
|
|
# TODO: ibv_resolve_eth_l2_from_gid function does not work.
|
|
# TODO: A few trivial functions (those ending in '_str') are currently implemented in HermitCore and
|
|
# do not forward the function call via uhyve_send. The generator does not yet take this into
|
|
# account. Refer to kernel/ibv.c -> ibv_wc_status_str() as an example.
|
|
|
|
|
|
# Path of the input file containing function prototypes.
|
|
SRC_PATH = "function-prototypes.txt"
|
|
|
|
# Paths of the files that are generated by the script.
|
|
KERNEL_GEN_PATH = "GEN-kernel.c"
|
|
KERNEL_HEADER_GEN_PATH = "GEN-kernel-header.h"
|
|
UHYVE_CASES_GEN_PATH = "GEN-tools-uhyve.c"
|
|
UHYVE_IBV_HEADER_GEN_PATH = "GEN-tools-uhyve-ibv-ports.h"
|
|
INCLUDE_STDDEF_GEN_PATH = "GEN-include-hermit-stddef.h"
|
|
UHYVE_HOST_FCNS_GEN_PATH = "GEN-tools-uhyve-ibv.c"
|
|
|
|
# Starting number of the sequence used for IBV ports.
|
|
PORT_NUMBER_START = 0x610
|
|
|
|
|
|
restricted_resources = ["struct ibv_send_wr",
|
|
"struct ibv_recv_wr",
|
|
"struct ibv_xrcd_init_attr",
|
|
"struct ibv_rwq_ind_table_init_attr",
|
|
|
|
# Deep resources that are not used as function parameters but are part of
|
|
# other resources that are used as parameters:
|
|
"struct ibv_sge",
|
|
"struct ibv_mw_bind_info",
|
|
"struct ibv_rx_hash_conf"]
|
|
|
|
lib_owned_resources = ["struct ibv_pd",
|
|
"struct ibv_cq",
|
|
"struct ibv_cq_ex",
|
|
"struct ibv_device",
|
|
"struct ibv_context",
|
|
"struct ibv_context_ops",
|
|
"struct ibv_flow",
|
|
"struct ibv_xrcd",
|
|
"struct ibv_mr",
|
|
"struct ibv_mw",
|
|
"struct ibv_comp_channel",
|
|
"struct ibv_srq",
|
|
"struct ibv_qp",
|
|
"struct ibv_wq",
|
|
"struct ibv_rwq_ind_table",
|
|
"struct ibv_ah"]
|
|
|
|
app_owned_resources = restricted_resources + \
|
|
["struct ibv_device_attr",
|
|
"struct ibv_device_attr_ex",
|
|
"struct ibv_port_attr",
|
|
"struct ibv_ah_attr",
|
|
"struct ibv_srq_attr",
|
|
"struct ibv_wq_attr",
|
|
"struct ibv_qp_attr",
|
|
"struct ibv_poll_cq_attr",
|
|
"struct ibv_flow_attr",
|
|
"struct ibv_global_route",
|
|
"struct ibv_packet_pacing_caps",
|
|
"struct ibv_qp_cap",
|
|
"struct ibv_odp_caps",
|
|
"struct ibv_tso_caps",
|
|
"struct ibv_rss_caps",
|
|
"struct ibv_flow_eth_filter",
|
|
"struct ibv_flow_spec_eth",
|
|
"struct ibv_flow_ipv4_filter",
|
|
"struct ibv_flow_spec_ipv4",
|
|
"struct ibv_flow_ipv4_ext_filter",
|
|
"struct ibv_flow_spec_ipv4_ext",
|
|
"struct ibv_flow_ipv6_filter",
|
|
"struct ibv_flow_spec_ipv6",
|
|
"struct ibv_flow_tcp_udp_filter",
|
|
"struct ibv_flow_spec_tcp_udp",
|
|
"struct ibv_flow_tunnel_filter",
|
|
"struct ibv_flow_spec_tunnel",
|
|
"struct ibv_flow_spec_action_tag",
|
|
"struct ibv_flow_spec_action_drop",
|
|
"struct ibv_query_device_ex_input",
|
|
"struct ibv_wc",
|
|
"struct ibv_grh",
|
|
"struct ibv_mw_bind",
|
|
"struct ibv_flow_spec",
|
|
"struct ibv_values_ex",
|
|
"struct ibv_rwq_ind_table_init_attr",
|
|
"struct ibv_xrcd_init_attr",
|
|
|
|
# Universal deep resources
|
|
# (contain valid pointers to lib owned resources, e.g. ibv_context):
|
|
"struct ibv_wq_init_attr",
|
|
"struct ibv_qp_init_attr",
|
|
"struct ibv_qp_init_attr_ex",
|
|
"struct ibv_srq_init_attr_ex",
|
|
"struct ibv_srq_init_attr",
|
|
"struct ibv_qp_open_attr",
|
|
"struct ibv_cq_init_attr_ex",
|
|
"struct ibv_async_event"]
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# CLASSES
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class Resource:
|
|
"""This class represents a verbs resource."""
|
|
def __init__(self, string, is_ret=False):
|
|
"""Initialize resource properties.
|
|
|
|
Members using ibv_context as an example:
|
|
full_expression: struct ibv_context * ctx
|
|
type: struct ibv_context *
|
|
name: ctx
|
|
struct_name: ibv_context
|
|
type_wo_asterisk: struct ibv_context
|
|
|
|
Args
|
|
string: Full string of the resource (like full expr. but spaces around '*' may be omitted).
|
|
is_ret: If True, a resource w/o name (like type) as is the case for return types is expected.
|
|
"""
|
|
if "**" in string:
|
|
string.replace("**", " ** ")
|
|
elif "*" in string:
|
|
string.replace("*", " * ")
|
|
|
|
self.components = string.split()
|
|
if is_ret:
|
|
self.components += [""]
|
|
|
|
self.full_expression = " ".join(self.components)
|
|
self.type = " ".join(self.components[:-1])
|
|
self.name = self.components[-1]
|
|
self.struct_name = self.components[1] if self.components[0] is "struct" else ""
|
|
self.type_wo_asterisk = (self.type[:-3] if self.is_ptr_ptr()
|
|
else self.type[:-2] if self.is_ptr()
|
|
else self.type)
|
|
|
|
def is_struct(self):
|
|
return self.components[0] == "struct"
|
|
|
|
def is_ptr(self):
|
|
return "*" in self.full_expression
|
|
|
|
def is_ptr_ptr(self):
|
|
return "**" in self.full_expression
|
|
|
|
def is_char_arr(self): # TODO: do something about these?
|
|
return self.is_ptr() and "char" in self.components
|
|
|
|
def is_void(self):
|
|
return self.type == "void"
|
|
|
|
def is_app_owned(self):
|
|
return self.type_wo_asterisk in app_owned_resources
|
|
|
|
def is_lib_owned(self):
|
|
return self.type_wo_asterisk in lib_owned_resources
|
|
|
|
def is_restricted(self):
|
|
return self.type_wo_asterisk in restricted_resources
|
|
|
|
class Function:
|
|
def __init__(self, string):
|
|
parens_split = string.split("(")
|
|
ret_and_name = parens_split[0].split(" ")
|
|
params_single_string = parens_split[-1].split(")")[0]
|
|
params_strings = params_single_string.split(",") if params_single_string else []
|
|
|
|
# Basic members (return type, function name, parameters)
|
|
self.ret = Resource(" ".join(ret_and_name[:-1]), is_ret=True)
|
|
self.name = ret_and_name[-1]
|
|
self.params = [Resource(p) for p in params_strings]
|
|
|
|
# Helper members, constructed from basic members
|
|
self.params_str = ", ".join([param.full_expression for param in self.params])
|
|
self.param_types = [param.type for param in self.params]
|
|
self.args_struct_name = "uhyve_{0}_t".format(self.name)
|
|
self.args_struct = self.__generate_args_struct()
|
|
self.port_name = "UHYVE_PORT_" + self.name.upper()
|
|
self.uhyve_fnc_name = "call_{0}".format(self.name)
|
|
self.uhyve_fnc_decl = ("void {0}(struct kvm_run * run, uint8_t * guest_mem);\n"
|
|
.format(self.uhyve_fnc_name))
|
|
self.fnc_decl = "{} {}({});".format(self.ret.type, self.name, self.params_str)
|
|
|
|
def __generate_args_struct(self):
|
|
"""Generates the arguments struct to hold a function's parameters and return value.
|
|
|
|
Returns:
|
|
Generated struct [string].
|
|
"""
|
|
code = "typedef struct {\n"
|
|
|
|
if len(self.params) > 0:
|
|
code += "\t// Parameters:\n"
|
|
for param in self.params:
|
|
code += "\t{0};\n".format(param.full_expression)
|
|
|
|
if not self.ret.is_void():
|
|
code += "\t// Return value:\n"
|
|
code += "\t{0} ret;\n".format(self.ret.type)
|
|
|
|
code += "}} __attribute__((packed)) {0};\n\n".format(self.args_struct_name)
|
|
return code
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# HELPER
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def generate_pretty_comment(string):
|
|
"""Generates a 3 line pretty comment of the given string."""
|
|
return "/*\n * {0}\n */\n\n".format(string)
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# CODE GENERATION: Expecting list of all functions
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def generate_hermit_function_declarations(functions):
|
|
"""Generates all HermitCore function declarations for the ibv.h header.
|
|
|
|
Args:
|
|
functions: List of all Functions [class Function].
|
|
"""
|
|
code = ""
|
|
for fnc in functions:
|
|
code += fnc.fnc_decl + "\n"
|
|
return code
|
|
|
|
def generate_port_enum(functions):
|
|
"""Generates the enum mapping KVM exit IO port names to port numbers.
|
|
|
|
Args:
|
|
functions: List of all Functions [class Function].
|
|
Returns:
|
|
Entire enum [string].
|
|
"""
|
|
code = "typedef enum {\n"
|
|
code += "\tUHYVE_PORT_SET_IB_POOL_ADDR = 0x{0},\n".format(format(PORT_NUMBER_START, "X"))
|
|
for num, fnc in enumerate(functions, PORT_NUMBER_START + 1):
|
|
code += "\t{0} = 0x{1},\n".format(fnc.port_name, format(num, "X"))
|
|
code += "} uhyve_ibv_t;"
|
|
|
|
return code
|
|
|
|
def generate_port_macros(functions):
|
|
"""Generates the compiler macros mapping KVM exit IO port names to port numbers.
|
|
|
|
Args:
|
|
functions: List of all Functions [class Function].
|
|
Returns:
|
|
Generated list of compiler macros [string].
|
|
"""
|
|
code = "#define UHYVE_PORT_SET_IB_POOL_ADDR 0x{0}\n".format(format(PORT_NUMBER_START, "X"))
|
|
for num, fnc in enumerate(functions, PORT_NUMBER_START + 1):
|
|
code += "#define {0} 0x{1}\n".format(fnc.port_name, format(num, "X"))
|
|
return code
|
|
|
|
def generate_uhyve_cases(functions):
|
|
""" Generates all switch-cases for the given verbs functions for uhyve's KVM exit IO.
|
|
|
|
Args:
|
|
functions: List of all Functions [class Function].
|
|
Returns:
|
|
Generated switch-cases [string]
|
|
"""
|
|
code = "\t\t\tcase UHYVE_PORT_SET_IB_POOL_ADDR: {\n"
|
|
code += "\t\t\t\t\tunsigned data = *((unsigned*)((size_t)run+run->io.data_offset));\n"
|
|
code += "\t\t\t\t\tuint64_t * temp = (uint64_t*)(guest_mem + data);\n"
|
|
code += "\t\t\t\t\tib_pool_addr = (uint8_t*) *temp;\n"
|
|
code += "\t\t\t\t\tib_pool_top = ib_pool_addr;\n"
|
|
code += "\t\t\t\t\tbreak;\n"
|
|
code += "\t\t\t}\n\n"
|
|
|
|
for fnc in functions:
|
|
code += "\t\t\tcase {0}:\n".format(fnc.port_name)
|
|
code += "\t\t\t\t{0}(run, guest_mem);\n".format(fnc.uhyve_fnc_name)
|
|
code += "\t\t\t\tbreak;\n"
|
|
|
|
return code
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# CODE GENERATION: Expecting a single function
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def generate_hermit_function_definition(fnc):
|
|
"""Generates the given verbs API function for HermitCore.
|
|
|
|
The function performs guest->host address conversions for all application owned resources that
|
|
are not restricted deep resources, i.e. that require no further processing of references.
|
|
It writes the arguemnts struct to the function's KVM I/O port, causing a KVM exit in uhyve.
|
|
|
|
Returns:
|
|
Generated full function definition [string].
|
|
"""
|
|
code = "{0} {1}({2}) {{\n".format(fnc.ret.type, fnc.name, fnc.params_str)
|
|
code += "\t{0} uhyve_args;\n".format(fnc.args_struct_name)
|
|
|
|
for param in fnc.params:
|
|
if param.is_ptr() and not param.is_lib_owned():
|
|
code += "\tuhyve_args.{0} = ({1}) guest_to_host((size_t) {0});".format(
|
|
param.name, param.type)
|
|
if param.is_ptr_ptr():
|
|
code += " // TODO: Check ** param here."
|
|
code += "\n"
|
|
else:
|
|
code += "\tuhyve_args.{0} = {0};\n".format(param.name)
|
|
code += "\n"
|
|
|
|
if fnc.name in custom_snippets.supported_functions:
|
|
code += custom_snippets.generate(fnc.name, custom_snippets.CONVERT)
|
|
|
|
code += ("\tuhyve_send({0}, (unsigned) virt_to_phys((size_t) &uhyve_args));\n\n"
|
|
.format(fnc.port_name))
|
|
|
|
if fnc.name in custom_snippets.supported_functions:
|
|
code += custom_snippets.generate(fnc.name, custom_snippets.REVERT)
|
|
|
|
if not fnc.ret.is_void():
|
|
code += "\treturn uhyve_args.ret;\n"
|
|
code += "}\n\n"
|
|
|
|
return code
|
|
|
|
def generate_uhyve_function(fnc):
|
|
"""Generates the uhyve function definition including the native library call.
|
|
|
|
Args:
|
|
fnc: A verbs function [class Function].
|
|
Returns:
|
|
Full function definition.
|
|
"""
|
|
code = generate_pretty_comment(fnc.name)
|
|
code += "void call_{0}(struct kvm_run * run, uint8_t * guest_mem) {{\n".format(fnc.name)
|
|
code += "\tprintf(\"LOG: UHYVE - call_{0}\\n\");\n".format(fnc.name) # TODO: Delete later.
|
|
code += "\tunsigned data = *((unsigned*) ((size_t) run + run->io.data_offset));\n"
|
|
code += "\t{0} * args = ({0} *) (guest_mem + data);\n\n".format(fnc.args_struct_name)
|
|
code += "\tuse_ib_mem_pool = true;\n"
|
|
|
|
code += "\t" + ("args->ret = " if not fnc.ret.is_void() else "") + "{0}(".format(fnc.name)
|
|
if len(fnc.params) > 0:
|
|
for param in fnc.params[:-1]:
|
|
code += "args->{}, ".format(param.name)
|
|
code += "args->{}".format(fnc.params[-1].name)
|
|
code += ");\n"
|
|
|
|
code += "\tuse_ib_mem_pool = false;\n}\n\n\n"
|
|
return code
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# MAIN
|
|
# ----------------------------------------------------------------------------
|
|
|
|
if __name__ == "__main__":
|
|
functions = []
|
|
|
|
# Parse the input file containing all verbs function declarations (one per line).
|
|
with open(SRC_PATH, "r") as f:
|
|
for line in f:
|
|
if line:
|
|
functions.append(Function(line))
|
|
|
|
# Generate code in different 'GEN-' files corresponding to actual source files.
|
|
with open(UHYVE_CASES_GEN_PATH, "w") as f:
|
|
f.write(generate_uhyve_cases(functions))
|
|
|
|
with open(INCLUDE_STDDEF_GEN_PATH, "w") as f:
|
|
f.write(generate_port_macros(functions))
|
|
|
|
with open(KERNEL_HEADER_GEN_PATH, "w") as f:
|
|
f.write(generate_hermit_function_declarations(functions))
|
|
|
|
with open(UHYVE_IBV_HEADER_GEN_PATH, "w") as f:
|
|
f.write(generate_port_enum(functions))
|
|
f.write("\n\n")
|
|
|
|
for fnc in functions:
|
|
f.write(fnc.args_struct)
|
|
f.write("\n\n")
|
|
|
|
for fnc in functions:
|
|
f.write(fnc.uhyve_fnc_decl)
|
|
|
|
with open(UHYVE_HOST_FCNS_GEN_PATH, "w") as f:
|
|
for fnc in functions:
|
|
f.write(generate_uhyve_function(fnc))
|
|
|
|
with open(KERNEL_GEN_PATH, "w") as f:
|
|
for fnc in functions:
|
|
f.write(generate_pretty_comment(fnc.name))
|
|
f.write(fnc.args_struct)
|
|
f.write(generate_hermit_function_definition(fnc))
|
|
f.write("\n")
|