Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update kraft for EPT. #1

Open
wants to merge 3 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 137 additions & 3 deletions kraft/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@


class Application(Component):
# path of file where the names of functions called via gate are collected
VMEPT_FUNC_LIST_PATH = "/tmp/flexos_vmept_rpc_id_data"
VMEPT_FUNC_FUNC_WLIST_PATH = "/tmp/flexos_vmept_wl"

# name and path of auto-generated header file containing the function addresses
VMEPT_ADDR_TABLE_NAME = "vmept_addr_table"

# where to put auto-generated header files (relative to the unikraft build directory)
VMEPT_AUTOGEN_INCLUDE_PATH = "lib/flexos-core/include/flexos/impl"

_type = ComponentType.APP

_config = None
Expand Down Expand Up @@ -605,6 +615,31 @@ def fetch(ctx, self, verbose=False):

return self.make(extra, verbose)

# generate a header containing an array with the addresses of functions called via EPT-gate
def vmept_gen_address_table_header(self, func_names, header_name, addr_map = {}, write_to_file = True, backuppath = None):
include_guard = "%s_H" % header_name.upper()
header_code = "#ifndef %s\n" % include_guard
header_code += "#define %s\n\n" % include_guard
header_code += 'static const void* flexos_vmept_addr_table[] __attribute__((section (".rodata"))) = {\n'
num_entries = len(func_names)
i = 0
for fname in func_names:
i += 1
sep = "," if i < num_entries else " "
addr = addr_map[fname] if fname in addr_map else 0
header_code += "\t(void*) 0x%016x%s /* %s */\n" % (addr, sep, fname)
header_code += "};\n\n"
header_code += "#endif /* %s */" % include_guard
if write_to_file:
addr_table_path = os.path.join(self.unikraft.localdir, self.VMEPT_AUTOGEN_INCLUDE_PATH)
addr_table_path = os.path.join(addr_table_path, self.VMEPT_ADDR_TABLE_NAME + ".h")
with open(addr_table_path, "w") as addr_table_file:
addr_table_file.write(header_code)
if backuppath is not None:
with open(backuppath, "w") as backupfile:
backupfile.write(header_code)
return header_code

@click.pass_context
def compartmentalize(ctx, self):
self.find_files()
Expand Down Expand Up @@ -792,29 +827,72 @@ def simple_replace(template_path, path, marker, shstack_enabled=True):
cmd = ["diff", "-urNp", backup_src, filep]
subprocess.call(cmd, stdout=fulldiff, stderr=subprocess.STDOUT)

is_ept = type(self.compartments[0].mechanism.driver) == VMEPTDriver
if is_ept and not os.path.isfile(self.VMEPT_FUNC_LIST_PATH):
# make sure this file exists
rpc_id_file = open(self.VMEPT_FUNC_LIST_PATH, "w")
rpc_id_file.close()

# now do library-specific rewrites
#TODO remove
logger.debug("## libraries ##")
for lib in self.libraries:
# TODO remove
logger.debug(lib.name)

# first add per-library linker scripts
if (not lib.compartment.default):
add_local_linkerscript(lib, fulldiff=fulldiff)

# then generate cocci files dynamically from the template
gr_rule_template = get_sec_rule("gatereplacer.cocci.in")
if is_ept:
gr_rule_template = get_sec_rule("gatereplacer_ept.cocci.in")
else:
gr_rule_template = get_sec_rule("gatereplacer.cocci.in")
if FCALLS_enabled:
cb_rule_template = get_sec_rule("rmcallbacks.cocci.in")
elif is_ept:
cb_rule_template = get_sec_rule("callbackreplacer_ept.cocci.in")
else:
cb_rule_template = get_sec_rule("callbackreplacer.cocci.in")

gr_rule_template = gr_rule_template.replace("{{ comp_cur_nb }}",
str(lib.compartment.number))
ept_rpc_id_prefix = "_RPC_ID_" if is_ept else ""
cb_rule_template = cb_rule_template.replace("{{ comp_cur_nb }}",
str(lib.compartment.number))
gr_rule = ""

if is_ept:

logger.debug("%s compartments" % str(len(self.compartments)))
rpc_id_gen_template = get_sec_rule("rpc_id_gen.cocci.in")

rpc_id_gen_template = rpc_id_gen_template.replace("{{ comp_cnt }}",
str(len(self.compartments)))
rpc_id_gen_template = rpc_id_gen_template.replace("{{ filename }}",
"'" + self.VMEPT_FUNC_LIST_PATH + "'")
rpc_id_gen_template = rpc_id_gen_template.replace("{{ func_wl_base }}",
"'" + self.VMEPT_FUNC_FUNC_WLIST_PATH + "'")
gr_rule = rpc_id_gen_template + "\n"

# for EPT calls to some libraries are always local
# FIXME some libraries are only temporarily here to build nginx/redis
ept_defaultlibs = [ "libukdebug", "ukdebug"
, "libuklock", "uklock", "liblock"
, "libukswrand", "ukswrand"
, "libukplat", "ukplat"
, "libuksched", "uksched"
, "ukboot", "libukboot"
#, "ukmpi", "libukmpi"
#, "libc"
]
def gr_gen_rule(dest_name, dest_comp):
gr_rule = str(gr_rule_template)
name = dest_name

if (dest_name != "lname" and not dest_name.startswith("app-")):
if (dest_name != "lname" and not dest_name.startswith("app-")
and not dest_name.startswith("lib") and (dest_name not in ept_defaultlibs)):
name = "lib" + name.replace("-", "")

gr_rule = gr_rule.replace("{{ lib_dest_name }}", name)
Expand All @@ -825,6 +903,17 @@ def gr_gen_rule(dest_name, dest_comp):
if dest_comp == lib.compartment:
# FIXME magic value, put somewhere
gr_gate = "flexos_nop_gate"
gr_rule = gr_rule.replace("{{ ept_id_prefix }}", "")
elif is_ept:
if (name in ept_defaultlibs):
# TODO remove
logger.debug("inserted nop_gate for %s (rewriting lib: %s)" % (name, lib.name))
gr_gate = "flexos_nop_gate"
gr_rule = gr_rule.replace("{{ ept_id_prefix }}", "")
else:
gr_rule = gr_rule.replace("{{ ept_id_prefix }}", ept_rpc_id_prefix)
# TODO remove
logger.debug("inserted vmept_gate for %s (rewriting lib: %s)" % (name, lib.name))

gr_rule = gr_rule.replace("{{ gate }}", gr_gate)
gr_rule = gr_rule.replace("{{ gate_r }}", gr_gate + "_r")
Expand All @@ -841,7 +930,8 @@ def cb_gen_rule(dest_name, dest_comp):
name = dest_name.replace("-", "")

if (dest_name != "lname" and not dest_name.startswith("app")
and not dest_name.startswith("lib")):
and not dest_name.startswith("lib")
and (dest_name not in ept_defaultlibs)):
name = "lib" + name

cb_rule = cb_rule.replace("{{ lib_from_name }}", name)
Expand All @@ -852,6 +942,17 @@ def cb_gen_rule(dest_name, dest_comp):
if dest_comp == lib.compartment:
# FIXME magic value, put somewhere
cb_gate = "flexos_nop_gate"
cb_rule = cb_rule.replace("{{ ept_id_prefix }}", "")
elif is_ept:
if (name in ept_defaultlibs):
# TODO remove
logger.debug("inserted nop_gate for %s (callback, rewriting lib: %s)" % (name, lib.name))
cb_gate = "flexos_nop_gate"
cb_rule = cb_rule.replace("{{ ept_id_prefix }}", "")
else:
# TODO remove
logger.debug("inserted vmept_gate for %s (callback, rewriting lib: %s)" % (name, lib.name))
cb_rule = cb_rule.replace("{{ ept_id_prefix }}", ept_rpc_id_prefix)

cb_rule = cb_rule.replace("{{ gate }}", cb_gate)
cb_rule = cb_rule.replace("{{ gate_r }}", cb_gate + "_r")
Expand All @@ -868,6 +969,7 @@ def cb_gen_rule(dest_name, dest_comp):
# with future upstream releases of Coccinelle, talking about it with Julia
whitelisted_libs = ["libvfscore", "libuknetdev", "newlib",
"libuksched", "libuksignal", "libukboot"]

for dest_lib in self.libraries:
if (not dest_lib.compartment.default) and (dest_lib.name != lib.name):
# this library is not in the default compartment, add a specific rule
Expand All @@ -885,6 +987,10 @@ def cb_gen_rule(dest_name, dest_comp):

# default rule, lname will match anything so this rule has to be at the end
default_comp = [comp for comp in self.compartments if comp.default][0]
if is_ept:
# this increases the number of rules but is needed for EPT
for libname in ept_defaultlibs:
gr_rule += gr_gen_rule(libname, default_comp)
gr_rule += gr_gen_rule("lname", default_comp)

# add malloc/free/calloc replacement rule
Expand Down Expand Up @@ -920,6 +1026,34 @@ def cb_gen_rule(dest_name, dest_comp):

coccinelle_rewrite(lib, rule_file, fulldiff)

# generate a header with macros defining IDs for all
# functions given by function_names
def vmept_gen_rpc_id_macro_header(function_names, header_name):
include_guard = "%s_H" % header_name.upper()
header_code = "#ifndef %s\n" % include_guard
header_code += "#define %s\n\n" % include_guard
rpc_id = 0
for fname in function_names:
header_code += "#define %s%s %d\n" % (ept_rpc_id_prefix, fname, rpc_id)
rpc_id += 1
header_code += "\n#define FLEXOS_VMEPT_RPCID_CNT %d\n\n" % rpc_id
header_code += "#endif /* %s */" % include_guard
return header_code

if (is_ept):
rpc_id_file = open(self.VMEPT_FUNC_LIST_PATH, "r")
fnames = list(map(str.strip, rpc_id_file.readlines()))
rpc_id_file.close()
id_header_name = "vmept_rpc_id"
addr_table_name = "flexos_%s" % self.VMEPT_ADDR_TABLE_NAME
id_header_code = vmept_gen_rpc_id_macro_header(fnames, id_header_name)
addr_table_code = self.vmept_gen_address_table_header(fnames, addr_table_name, write_to_file = True)
id_header_path = os.path.join(self.unikraft.localdir, self.VMEPT_AUTOGEN_INCLUDE_PATH)
id_header_path = os.path.join(id_header_path, id_header_name + ".h")
id_header_file = open(id_header_path, "w")
id_header_file.write(id_header_code)
id_header_file.close()

logger.info("Full diff of rewritings: %s" % fulldifff)
fulldiff.close()

Expand Down
68 changes: 67 additions & 1 deletion kraft/cmd/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,35 @@ def simple_replace(template_path, marker):
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

appcomp = None
appcompnum = 0 # assume app is in comp0
for lib in app.libraries:
# TODO
logger.info("lib: %s, compartment: %s" % (lib.name, str(lib.compartment.number)))
if lib.name.startswith("app"):
appcomp = lib.compartment

if appcomp is not None:
appcompnum = appcomp.number

# get VMEPT config options
max_threads_shift = 8 # max hreads is (1 << max_threads_shift)
with open(os.path.join(app.localdir, ".config")) as conf:
conf_data = conf.read()
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_256=y' in conf_data:
max_threads_shift = 8
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_128=y' in conf_data:
max_threads_shift = 7
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_64=y' in conf_data:
max_threads_shift = 6
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_32=y' in conf_data:
max_threads_shift = 5
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_16=y' in conf_data:
max_threads_shift = 4
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_8=y' in conf_data:
max_threads_shift = 3
if 'CONFIG_LIBFLEXOS_VMEPT_MAX_THREADS_4=y' in conf_data:
max_threads_shift = 2

i = 0
logger.info("Building with VM/EPT: " + str(no_ept) + " images to build.")
for comp in app.compartments:
Expand All @@ -92,7 +117,8 @@ def simple_replace(template_path, marker):
# edit build args
extra_args = ["CFLAGS_EXTRA=-DFLEXOS_VMEPT_COMP_ID=" + str(i)
+ " -DFLEXOS_VMEPT_COMP_COUNT=" + str(no_ept)
+ " -DFLEXOS_VMEPT_APPCOMP=" + str(appcomp.number)]
+ " -DFLEXOS_VMEPT_APPCOMP=" + str(appcompnum)
+ " -DFLEXOS_VMEPT_MAX_THREADS_SHIFT=" + str(max_threads_shift)]
if fast:
extra_args.insert(0, "-j")

Expand All @@ -111,6 +137,46 @@ def simple_replace(template_path, marker):
"of the make command (code " + str(return_code) + ")")
return

for target in app.binaries:
# insert correct addresses
with open(Application.VMEPT_FUNC_LIST_PATH, "r") as funcname_file:
fnames = list(map(str.strip, funcname_file.readlines()))

with open(Application.VMEPT_FUNC_FUNC_WLIST_PATH + str(i), "r") as wl_file:
fnames_wl = list(map(str.strip, wl_file.readlines()))

addr_map = {}
for fname in fnames:
addr_map[fname] = 0

readelf = subprocess.Popen(['readelf', '-sW', target.binary_debug], stdout = subprocess.PIPE)
output = subprocess.check_output(['awk', '{if ($4 == "FUNC") { print $2,$8 } }'], stdin = readelf.stdout).decode()
readelf.wait()

raw_lines = output.splitlines()

entries = []
for line in raw_lines:
fields = line.split()
address = int(fields[0], 16)
name = fields[1]
if name in addr_map and name in fnames_wl:
addr_map[name] = address

patched_code = app.vmept_gen_address_table_header(fnames,
Application.VMEPT_ADDR_TABLE_NAME, addr_map,
write_to_file = True, backuppath = "/tmp/flexos_vmept_addr_table_" + str(i))

# build again
return_code = make_progressbar(app.make_raw(verbose=verbose,
extra=extra_args))

if (return_code > 0):
# there was probably an error
logger.error("Aborting build due to probable error during execution "
"of the make command (code " + str(return_code) + ")")
return

# secure image (so that it doesn't get overwritten)
for target in app.binaries:
os.rename(target.binary, target.binary + ".comp" + str(i))
Expand Down
4 changes: 3 additions & 1 deletion kraft/sec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ def coccinelle_rewrite(lib, rule_file, fulldiff):

with open(log, 'wb') as logf:
try:
cmd1 = ["spatch", "-j", "6", "-in_place", "-sp_file", rule_file, file]
# we can't use parallel jobs when building for EPT since this leads to
# @finalize scripts being called too early
cmd1 = ["spatch", "-in_place", "-sp_file", rule_file, file]
cmd2 = ["diff", "-urNp", backup_src, file]
logf.write(bytes("$ " + " ".join(cmd1) + "\n", 'utf-8'))
logf.flush()
Expand Down
2 changes: 1 addition & 1 deletion kraft/sec/replacements/ukboot_init_sections.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
(void *) __uk_image_symbol(_ecomp{{ comp_nr }}));
PROTECT_SECTION("bss_comp{{ comp_nr }}", {{ comp_nr }}, (void *) __uk_image_symbol(_bss_comp{{ comp_nr }}),
(void *) __uk_image_symbol(_ebss_comp{{ comp_nr }}));
ASSIGN_HEAP("comp{{ comp_nr }}", {{ comp_nr }} /* key */, 1000 /* size */, flexos_comp{{ comp_nr }}_alloc);
ASSIGN_HEAP("comp{{ comp_nr }}", {{ comp_nr }} /* key */, CONFIG_LIBFLEXOS_COMP_HEAP_SIZE /* size */, flexos_comp{{ comp_nr }}_alloc);
Loading