1
0
Fork 0
mirror of https://github.com/hermitcore/libhermit.git synced 2025-03-30 00:00:15 +01:00
libhermit/tools/ibv_code_generator/generate-code.py

452 lines
15 KiB
Python
Executable file

#!/usr/bin/env python
"""Copyright (c) 2017, 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.
This script expects a text file containing function prototypes as input
(SRC_PATH). It generates the following C code snippets for each individual given
function in the input file. Todo notes are inserted whereever more work is
required.
1. The definition of a struct that contains all parameters and the return value
of a given function.
Required in: ./kernel/ibv.c
Example:
typedef struct {
// Parameters:
struct ibv_mr * mr;
int flags;
struct ibv_pd * pd;
// Return value:
int ret;
} __attribute__((packed)) uhyve_ibv_rereg_mr_t;
2. The definition of the kernel space function that sends a KVM exit IO to
uhyve.
Required in: ./kernel/ibv.c
Example:
int ibv_rereg_mr(struct ibv_mr * mr, int flags, struct ibv_pd * pd) {
uhyve_ibv_rereg_mr_t uhyve_args;
uhyve_args->mr = (struct ibv_mr *) virt_to_phys((size_t) mr);
uhyve_args->flags = flags;
uhyve_args->pd = (struct ibv_pd *) virt_to_phys((size_t) pd);
uhyve_send(UHYVE_PORT_IBV_REREG_MR, (unsigned) virt_to_phys((size_t) &uhyve_args));
return uhyve_args.ret;
}
3. TODO The switch-case that catches the KVM exit IO sent to uhyve by the kernel.
Required in: ./tool/uhyve.c
Example:
case UHYVE_PORT_IBV_REREG_MR: {
unsigned data = *((unsigned*)((size_t)run+run->io.data_offset));
uhyve_ibv_rereg_mr_t * args = (uhyve_ibv_rereg_mr_t *) (guest_mem + data);
int host_ret = ibv_rereg_mr(guest_mem+(size_t)args->mr, flags, guest_mem+(size_t)args->pd);
args->ret = host_ret;
break;
}
The script also generates an enum mapping all functions to KVM exit IO port
names and numbers.
Required in: ./tool/uhyve-ibv.h
Example:
typedef enum {
UHYVE_PORT_IBV_WC_STATUS_STR = 0x510,
UHYVE_PORT_IBV_RATE_TO_MULT = 0x511,
UHYVE_PORT_MULT_TO_IBV_RATE = 0x512,
// ...
} uhyve_ibv_t;
"""
# TODO: Add ibv_resolve_eth_l2_from_gid function back in. Not linking right now.
from __future__ import print_function
from parser import generate_struct_conversions
# 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_IBV_HEADER_STRUCTS_GEN_PATH = "GEN-tools-uhyve-ibv-structs.h"
UHYVE_HOST_FCNS_GEN_PATH = "GEN-tools-uhyve-ibv.c"
# VERBS_HEADER_PATH = "verbs-0.h"
# Starting number of the sequence used for IBV ports.
PORT_NUMBER_START = 0x610
TABS = ["", "\t", "\t\t", "\t\t\t", "\t\t\t\t"]
NEWLINES = ["", "\n", "\n\n"]
params_in_guest_mem = ['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',
# containing ib pool pointers:
'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']
class Type:
def __init__(self, string):
ts = string
# if len(string) > 2 and string[-1] is "*":
# if string[-2] is "*" and string[-3] is not " ":
# ts = string[:-2] + " **"
# elif string[-2] is not " ":
# ts = string[:-1] + " *"
self.type_string = ts
self.type_components = ts.split(" ")
def get_struct_name(self):
name = ""
if self.is_struct():
name = self.type_components[1]
return name
def is_struct(self):
return self.type_components[0] == "struct"
def is_char_arr(self):
return self.is_pointer() and "char" in self.type_components
def is_pointer(self):
return self.type_components[-1] == "*"
def is_pointer_pointer(self):
return self.type_components[-1] == "**"
def is_void(self):
return self.type_string == "void"
class FunctionParameter:
def __init__(self, string):
components = string.split(" ")
type_string = " ".join(components[:-1])
self.type = Type(type_string)
self.name = components[-1]
def get_full_expression(self):
return self.type.type_string + " " + self.name
def get_struct_name(self):
return self.type.get_struct_name()
def is_struct(self):
return self.type.is_struct()
def is_pointer(self):
return self.type.is_pointer()
def is_pointer_pointer(self):
return self.type.is_pointer_pointer()
class FunctionPrototype:
def __init__(self, string):
parens_split = string.split("(")
ret_and_name = parens_split[0].split(" ")
all_params = parens_split[-1].split(")")[0]
if all_params:
param_strings = all_params.split(",")
else:
param_strings = []
self.parameters = [FunctionParameter(p) for p in param_strings]
self.ret = Type(" ".join(ret_and_name[:-1]))
self.function_name = ret_and_name[-1]
def generate_args_struct(self):
"""Generates the struct to hold a function's parameters and return value.
Returns:
Generated struct as string.
"""
code = "typedef struct {\n"
if self.get_num_parameters() > 0:
code += "\t// Parameters:\n"
for param in self.parameters:
code += "\t{0};\n".format(param.get_full_expression())
if not self.ret.is_void():
code += "\t// Return value:\n"
code += "\t{0} ret;\n".format(self.ret.type_string)
code += "}} __attribute__((packed)) {0};\n\n".format(self.get_args_struct_name())
return code
def generate_function_declaration(self):
return "{} {}({});\n".format(self.ret.type_string, self.function_name,
self.get_string_of_parameters())
def get_string_of_parameters(self):
return ", ".join([param.get_full_expression() for param in self.parameters])
def generate_uhyve_function_declaration(self):
name = self.get_uhyve_call_function_name()
return "void {0}(struct kvm_run * run, uint8_t * guest_mem);\n".format(name)
def get_uhyve_call_function_name(self):
return "call_{0}".format(self.function_name)
def get_num_parameters(self):
return len(self.parameters)
def get_parameter_types(self):
return [param.type.type_string for param in self.parameters]
def get_port_name(self):
return "UHYVE_PORT_" + self.function_name.upper()
def get_args_struct_name(self):
return "uhyve_{0}_t".format(self.function_name)
# -----------------------------------------------------------------------------
def generate_pretty_comment(string):
return "/*\n * {0}\n */\n\n".format(string)
def generate_kernel_header_declarations(function_prototypes):
code = ""
for pt in function_prototypes:
code += pt.generate_function_declaration()
return code
def generate_kernel_function(function_prototype):
"""Generates the kernel function that sends the KVM exit IO to uhyve.
Returns:
Generated function as string.
"""
fnc_name = function_prototype.function_name
ret_type = function_prototype.ret
params = function_prototype.parameters
port_name = function_prototype.get_port_name()
comma_separated_params = function_prototype.get_string_of_parameters()
code = "{0} {1}({2}) {{\n".format(ret_type.type_string, fnc_name, comma_separated_params)
code += "\t{0} uhyve_args;\n".format(function_prototype.get_args_struct_name())
for p in params:
if p.is_pointer_pointer():
code += "\t// TODO: Take care of ** parameter.\n"
else:
if 'struct ' + p.get_struct_name() in params_in_guest_mem and p.is_pointer():
code += "\tuhyve_args.{0} = ({1}) guest_to_host((size_t) {0});\n".format(
p.name, p.type.type_string)
else:
code += "\tuhyve_args.{0} = {0};\n".format(p.name)
code += "\n"
code += "\tuhyve_send({0}, (unsigned) virt_to_phys((size_t) &uhyve_args));\n".format(port_name)
if not ret_type.is_void():
code += "\n\treturn uhyve_args.ret;\n"
code += "}\n\n"
return code
def generate_uhyve_cases(function_prototypes):
""" Generates all switch-cases for uhyve's KVM exit IO.
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 pt in function_prototypes:
call_fnc_name = pt.get_uhyve_call_function_name()
port_name = pt.get_port_name()
code += "\t\t\tcase {0}:\n".format(port_name)
code += "\t\t\t\t{0}(run, guest_mem);\n".format(call_fnc_name)
code += "\t\t\t\tbreak;\n"
return code
def generate_uhyve_function(prototype):
args_struct_name = prototype.get_args_struct_name()
fnc_name = prototype.function_name
ret_type = prototype.ret
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)
code += "\tunsigned data = *((unsigned*) ((size_t) run + run->io.data_offset));\n"
code += "\t{0} * args = ({0} *) (guest_mem + data);\n\n".format(args_struct_name)
code += "\tuse_ib_mem_pool = true;\n"
code += ("\t" + ("args->ret = " if not ret_type.is_void() else "")
+ "{0}(".format(fnc_name))
if prototype.get_num_parameters() > 0:
for param in prototype.parameters[:-1]:
code += "args->{}, ".format(param.name)
code += "args->{}".format(prototype.parameters[-1].name)
code += ");\n"
code += "\tuse_ib_mem_pool = false;\n}\n\n\n"
return code
def generate_port_enum(function_prototypes):
"""Generates the enum mapping KVM exit IO port names to port numbers.
Args:
function_prototypes: All function names to be mapped to ports as list of strings.
Returns:
Generated complete enum.
"""
code = "typedef enum {\n"
code += "\tUHYVE_PORT_SET_IB_POOL_ADDR = 0x{0},\n".format(format(PORT_NUMBER_START-1, "X"))
for num, pt in enumerate(function_prototypes, PORT_NUMBER_START):
port_name = pt.get_port_name()
code += "\t{0} = 0x{1},\n".format(port_name, format(num, "X"))
code += "} uhyve_ibv_t;"
return code
def generate_port_macros(function_prototypes):
"""Generates the compiler macros mapping KVM exit IO port names to port numbers.
Args:
function_names: All function names to be mapped to ports as list of strings.
Returns:
Generated list of compiler macros.
"""
code = "#define UHYVE_PORT_SET_IB_POOL_ADDR 0x{0}\n".format(format(PORT_NUMBER_START-1, "X"))
for num, pt in enumerate(function_prototypes, PORT_NUMBER_START):
port_name = pt.get_port_name()
code += "#define {0} 0x{1}\n".format(port_name, format(num, "X"))
return code
if __name__ == "__main__":
prototypes = []
with open(SRC_PATH, "r") as f:
for line in f:
if line:
prototypes.append(FunctionPrototype(line))
with open(UHYVE_CASES_GEN_PATH, "w") as f:
f.write(generate_uhyve_cases(prototypes))
with open(INCLUDE_STDDEF_GEN_PATH, "w") as f:
f.write(generate_port_macros(prototypes))
with open(UHYVE_IBV_HEADER_GEN_PATH, "w") as f:
f.write(generate_port_enum(prototypes))
f.write("\n\n")
for pt in prototypes:
f.write(pt.generate_args_struct())
f.write("\n\n")
for pt in prototypes:
f.write(pt.generate_uhyve_function_declaration())
with open(UHYVE_HOST_FCNS_GEN_PATH, "w") as f:
for pt in prototypes:
f.write(generate_uhyve_function(pt))
with open(KERNEL_HEADER_GEN_PATH, "w") as f:
f.write(generate_kernel_header_declarations(prototypes))
with open(KERNEL_GEN_PATH, "w") as f:
for pt in prototypes:
f.write(generate_pretty_comment(pt.function_name))
f.write(pt.generate_args_struct())
f.write(generate_kernel_function(pt))
f.write("\n")