From 35f71479c26c2d503cf2192e8c813229b73a62af Mon Sep 17 00:00:00 2001 From: Sebastian Rauch Date: Mon, 13 Dec 2021 23:15:03 +0100 Subject: [PATCH] Update kraft for EPT. Signed-off-by: Sebastian Rauch --- kraft/app/app.py | 77 +++++++++++++++++++ kraft/cmd/build.py | 59 +++++++++++++- kraft/sec/__init__.py | 4 +- .../sec/replacements/ukboot_init_sections.in | 2 +- kraft/sec/rules/gatereplacer.cocci.in | 60 ++++++++++++++- 5 files changed, 195 insertions(+), 7 deletions(-) diff --git a/kraft/app/app.py b/kraft/app/app.py index cb5df0a9..83e4e3fb 100644 --- a/kraft/app/app.py +++ b/kraft/app/app.py @@ -91,6 +91,15 @@ 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" + + # 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 @@ -605,6 +614,29 @@ 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): + 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") + addr_table_file = open(addr_table_path, "w") + addr_table_file.write(header_code) + addr_table_file.close() + return header_code + @click.pass_context def compartmentalize(ctx, self): self.find_files() @@ -792,6 +824,12 @@ 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 for lib in self.libraries: # first add per-library linker scripts @@ -810,6 +848,13 @@ def simple_replace(template_path, path, marker, shstack_enabled=True): str(lib.compartment.number)) gr_rule = "" + ept_rpc_id_prefix = "_RPC_ID_" if is_ept else "" + if (is_ept): + rpc_id_gen_template = get_sec_rule("rpc_id_gen.cocci.in") + rpc_id_gen_template = rpc_id_gen_template.replace("{{ filename }}", + "'" + self.VMEPT_FUNC_LIST_PATH + "'") + gr_rule = rpc_id_gen_template + "\n" + def gr_gen_rule(dest_name, dest_comp): gr_rule = str(gr_rule_template) name = dest_name @@ -825,6 +870,9 @@ 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 }}", "") + else: + gr_rule = gr_rule.replace("{{ ept_id_prefix }}", ept_rpc_id_prefix) gr_rule = gr_rule.replace("{{ gate }}", gr_gate) gr_rule = gr_rule.replace("{{ gate_r }}", gr_gate + "_r") @@ -868,6 +916,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 @@ -920,6 +969,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() diff --git a/kraft/cmd/build.py b/kraft/cmd/build.py index 9e39c4a9..574949f0 100644 --- a/kraft/cmd/build.py +++ b/kraft/cmd/build.py @@ -84,6 +84,25 @@ def simple_replace(template_path, marker): if lib.name.startswith("app"): appcomp = lib.compartment + # 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: @@ -92,7 +111,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(appcomp.number) + + " -DFLEXOS_VMEPT_MAX_THREADS_SHIFT=" + str(max_threads_shift)] if fast: extra_args.insert(0, "-j") @@ -111,6 +131,43 @@ def simple_replace(template_path, marker): "of the make command (code " + str(return_code) + ")") return + for target in app.binaries: + # insert correct addresses + funcname_file = open(Application.VMEPT_FUNC_LIST_PATH, "r") + fnames = list(map(str.strip, funcname_file.readlines())) + funcname_file.close() + + 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: + addr_map[name] = address + + patched_code = app.vmept_gen_address_table_header(fnames, + Application.VMEPT_ADDR_TABLE_NAME, addr_map, write_to_file = True) + + # 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)) diff --git a/kraft/sec/__init__.py b/kraft/sec/__init__.py index 17c97c63..3d088432 100644 --- a/kraft/sec/__init__.py +++ b/kraft/sec/__init__.py @@ -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() diff --git a/kraft/sec/replacements/ukboot_init_sections.in b/kraft/sec/replacements/ukboot_init_sections.in index 7b593f13..6641c615 100644 --- a/kraft/sec/replacements/ukboot_init_sections.in +++ b/kraft/sec/replacements/ukboot_init_sections.in @@ -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); diff --git a/kraft/sec/rules/gatereplacer.cocci.in b/kraft/sec/rules/gatereplacer.cocci.in index 6e226a3c..54d657ab 100644 --- a/kraft/sec/rules/gatereplacer.cocci.in +++ b/kraft/sec/rules/gatereplacer.cocci.in @@ -1,13 +1,65 @@ +@gatereplacer_return0_{{ rule_nr }}@ +identifier func; +expression ret, lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate_r({{ lib_dest_name }}, ret, func); ++ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, func_id); + +@script:python@ +func_name << gatereplacer_return0_{{ rule_nr }}.func; +@@ +fname = str(func_name) +if fname not in entries: + entries[fname] = entry_cnt + entry_cnt += 1 + @gatereplacer_return{{ rule_nr }}@ +identifier func; expression list EL; expression ret, lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate_r({{ lib_dest_name }}, ret, func, EL); ++ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, func_id, EL); + +@script:python@ +func_name << gatereplacer_return{{ rule_nr }}.func; @@ -- flexos_gate_r({{ lib_dest_name }}, ret, EL); -+ {{ gate_r }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, ret, EL); +fname = str(func_name) +if fname not in entries: + entries[fname] = entry_cnt + entry_cnt += 1 + +@gatereplacer_noreturn0_{{ rule_nr }}@ +identifier func; +expression lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate({{ lib_dest_name }}, func); ++ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, func_id); + +@script:python@ +func_name << gatereplacer_noreturn0_{{ rule_nr }}.func; +@@ +fname = str(func_name) +if fname not in entries: + entries[fname] = entry_cnt + entry_cnt += 1 @gatereplacer_noreturn{{ rule_nr }}@ +identifier func; expression list EL; expression lname; +fresh identifier func_id = "{{ ept_id_prefix }}" ## func; +@@ +- flexos_gate({{ lib_dest_name }}, func, EL); ++ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, func_id, EL); + +@script:python@ +func_name << gatereplacer_noreturn{{ rule_nr }}.func; @@ -- flexos_gate({{ lib_dest_name }}, EL); -+ {{ gate }}({{ comp_cur_nb }}, {{ comp_dest_nb }}, EL); +fname = str(func_name) +if fname not in entries: + entries[fname] = entry_cnt + entry_cnt += 1