diff --git a/os/include/loader_session/client.h b/os/include/loader_session/client.h index 98918bba6..56994742c 100644 --- a/os/include/loader_session/client.h +++ b/os/include/loader_session/client.h @@ -41,6 +41,9 @@ namespace Loader { void view_ready_sigh(Signal_context_capability sigh) { call(sigh); } + void fault_sigh(Signal_context_capability sigh) { + call(sigh); } + void start(Name const &binary, Name const &label = "", Native_pd_args const &pd_args = Native_pd_args()) { call(binary, label, pd_args); } diff --git a/os/include/loader_session/loader_session.h b/os/include/loader_session/loader_session.h index 344ba0ec6..59f51080d 100644 --- a/os/include/loader_session/loader_session.h +++ b/os/include/loader_session/loader_session.h @@ -116,6 +116,20 @@ namespace Loader { */ virtual void view_ready_sigh(Signal_context_capability sigh) = 0; + /** + * Register signal handler notified when a failure occurs in the + * loaded subsystem. + * + * This signal is delivered if any child process of the subsystem + * produces an unresolvable page fault, exists, or triggers a CPU + * exception. (e.g., division by zero) For more information about + * the possible types of faults, please refer to the documentation of + * 'Rm_session::fault_handler' and 'Cpu_session::exception_handler'. + * + * This function should not be called after the 'start' function. + */ + virtual void fault_sigh(Signal_context_capability sigh) = 0; + /** * Start subsystem * @@ -152,6 +166,7 @@ namespace Loader { GENODE_RPC(Rpc_ram_quota, void, ram_quota, size_t); GENODE_RPC(Rpc_constrain_geometry, void, constrain_geometry, int, int); GENODE_RPC(Rpc_view_ready_sigh, void, view_ready_sigh, Signal_context_capability); + GENODE_RPC(Rpc_fault_sigh, void, fault_sigh, Signal_context_capability); GENODE_RPC_THROW(Rpc_start, void, start, GENODE_TYPE_LIST(Rom_module_does_not_exist), Name const &, Name const &, Native_pd_args const &); @@ -161,8 +176,8 @@ namespace Loader { GENODE_RPC_INTERFACE(Rpc_alloc_rom_module, Rpc_commit_rom_module, Rpc_ram_quota, Rpc_constrain_geometry, - Rpc_view_ready_sigh, Rpc_start, Rpc_view, - Rpc_view_geometry); + Rpc_view_ready_sigh, Rpc_fault_sigh, Rpc_start, + Rpc_view, Rpc_view_geometry); }; } diff --git a/os/run/failsafe.run b/os/run/failsafe.run index d46f3a5ab..ebb3ebce0 100644 --- a/os/run/failsafe.run +++ b/os/run/failsafe.run @@ -8,7 +8,7 @@ # Build # -build { core init test/failsafe test/segfault } +build { core init server/loader test/failsafe test/segfault } create_boot_directory @@ -31,6 +31,10 @@ install_config { + + + + @@ -42,7 +46,7 @@ install_config { # # generic modules -set boot_modules { core init test-failsafe test-segfault } +set boot_modules { core init loader test-failsafe test-segfault } build_boot_image $boot_modules @@ -50,5 +54,7 @@ build_boot_image $boot_modules # Execute test case # -run_genode_until "--- finished failsafe test ---.*\n" 10 +append qemu_args "-nographic -m 64" + +run_genode_until "--- finished failsafe test ---.*\n" 30 diff --git a/os/src/server/loader/child.h b/os/src/server/loader/child.h index 0aa18f06c..f73c05e82 100644 --- a/os/src/server/loader/child.h +++ b/os/src/server/loader/child.h @@ -47,8 +47,9 @@ namespace Loader { Rm_connection rm; Resources(char const *label, - Ram_session_client &ram_session_client, - size_t ram_quota) + Ram_session_client &ram_session_client, + size_t ram_quota, + Signal_context_capability fault_sigh) : ram(label), cpu(label) { /* deduce session costs from usable ram quota */ @@ -62,6 +63,13 @@ namespace Loader { ram.ref_account(ram_session_client); ram_session_client.transfer_quota(ram.cap(), ram_quota); + + /* + * Install CPU exception and RM fault handler assigned by + * the loader client via 'Loader_session::fault_handler'. + */ + cpu.exception_handler(Thread_capability(), fault_sigh); + rm.fault_handler(fault_sigh); } } _resources; @@ -69,6 +77,8 @@ namespace Loader { Service_registry &_parent_services; Service &_local_nitpicker_service; Service &_local_rom_service; + Service &_local_cpu_service; + Service &_local_rm_service; Rom_session_client _binary_rom_session; @@ -94,25 +104,30 @@ namespace Loader { public: - Child(char const *binary_name, - char const *label, - Native_pd_args const &pd_args, - Rpc_entrypoint &ep, - Ram_session_client &ram_session_client, - size_t ram_quota, - Service_registry &parent_services, - Service &local_rom_service, - Service &local_nitpicker_service, - int max_width, - int max_height) + Child(char const *binary_name, + char const *label, + Native_pd_args const &pd_args, + Rpc_entrypoint &ep, + Ram_session_client &ram_session_client, + size_t ram_quota, + Service_registry &parent_services, + Service &local_rom_service, + Service &local_cpu_service, + Service &local_rm_service, + Service &local_nitpicker_service, + Signal_context_capability fault_sigh, + int max_width, + int max_height) : _label(label), _pd_args(pd_args), _ep(ep), - _resources(_label.string, ram_session_client, ram_quota), + _resources(_label.string, ram_session_client, ram_quota, fault_sigh), _parent_services(parent_services), _local_nitpicker_service(local_nitpicker_service), _local_rom_service(local_rom_service), + _local_cpu_service(local_cpu_service), + _local_rm_service(local_rm_service), _binary_rom_session(_rom_session(binary_name)), _binary_policy("binary", _binary_rom_session.dataspace(), &_ep), _labeling_policy(_label.string), @@ -173,11 +188,10 @@ namespace Loader { if ((service = _binary_policy.resolve_session_request(name, args))) return service; - if (!strcmp(name, "Nitpicker")) - return &_local_nitpicker_service; - - if (!strcmp(name, "ROM")) - return &_local_rom_service; + if (!strcmp(name, "Nitpicker")) return &_local_nitpicker_service; + if (!strcmp(name, "ROM")) return &_local_rom_service; + if (!strcmp(name, "CPU")) return &_local_cpu_service; + if (!strcmp(name, "RM")) return &_local_rm_service; /* populate session-local parent service registry on demand */ service = _parent_services.find(name); diff --git a/os/src/server/loader/main.cc b/os/src/server/loader/main.cc index 0cdf94c97..d6b33afe9 100644 --- a/os/src/server/loader/main.cc +++ b/os/src/server/loader/main.cc @@ -120,6 +120,54 @@ namespace Loader { void upgrade(Session_capability session, const char *) { } }; + /** + * Common base class of 'Local_cpu_service' and 'Local_rm_service' + */ + struct Intercepted_parent_service : Service + { + Signal_context_capability fault_sigh; + + Intercepted_parent_service(char const *name) : Service(name) { } + + void close(Session_capability session) + { + env()->parent()->close(session); + } + + void upgrade(Session_capability session, const char *) { } + }; + + /** + * Intercept CPU session requests to install default exception + * handler + */ + struct Local_cpu_service : Intercepted_parent_service + { + Local_cpu_service() : Intercepted_parent_service("CPU") { } + + Genode::Session_capability session(const char *args) + { + Capability cap = env()->parent()->session(args); + Cpu_session_client(cap).exception_handler(Thread_capability(), fault_sigh); + return cap; + } + }; + + /** + * Intercept RM session requests to install default fault handler + */ + struct Local_rm_service : Intercepted_parent_service + { + Local_rm_service() : Intercepted_parent_service("RM") { } + + Genode::Session_capability session(const char *args) + { + Capability cap = env()->parent()->session(args); + Rm_session_client(cap).fault_handler(fault_sigh); + return cap; + } + }; + struct Local_nitpicker_service : Service { Rpc_entrypoint &ep; @@ -172,7 +220,10 @@ namespace Loader { Service_registry _parent_services; Rom_module_registry _rom_modules; Local_rom_service _rom_service; + Local_cpu_service _cpu_service; + Local_rm_service _rm_service; Local_nitpicker_service _nitpicker_service; + Signal_context_capability _fault_sigh; Child *_child; /** @@ -244,6 +295,26 @@ namespace Loader { _nitpicker_service.view_ready_sigh = sigh; } + void fault_sigh(Signal_context_capability sigh) + { + /* + * CPU exception handler for CPU sessions originating from the + * subsystem. + */ + _cpu_service.fault_sigh = sigh; + + /* + * RM fault handler for RM sessions originating from the + * subsystem. + */ + _rm_service.fault_sigh = sigh; + + /* + * CPU exception and RM fault handler for the immediate child. + */ + _fault_sigh = sigh; + } + void start(Name const &binary_name, Name const &label, Genode::Native_pd_args const &pd_args) { @@ -261,7 +332,8 @@ namespace Loader { Child(binary_name.string(), label.string(), pd_args, _ep, _ram_session_client, ram_quota, _parent_services, _rom_service, - _nitpicker_service, _width, _height); + _cpu_service, _rm_service, _nitpicker_service, + _fault_sigh, _width, _height); } catch (Genode::Parent::Service_denied) { throw Rom_module_does_not_exist(); } diff --git a/os/src/test/failsafe/main.cc b/os/src/test/failsafe/main.cc index b82323912..4143bbd2a 100644 --- a/os/src/test/failsafe/main.cc +++ b/os/src/test/failsafe/main.cc @@ -19,8 +19,32 @@ #include #include #include +#include +/*************** + ** Utilities ** + ***************/ + +static void wait_for_signal_for_context(Genode::Signal_receiver &sig_rec, + Genode::Signal_context const &sig_ctx) +{ + Genode::Signal s = sig_rec.wait_for_signal(); + + if (s.num() && s.context() == &sig_ctx) { + PLOG("got exception for child"); + } else { + PERR("got unexpected signal while waiting for child"); + class Unexpected_signal { }; + throw Unexpected_signal(); + } +} + + +/****************************************************************** + ** Test for detecting the failure of an immediate child process ** + ******************************************************************/ + class Test_child : public Genode::Child_policy { private: @@ -40,18 +64,16 @@ class Test_child : public Genode::Child_policy ram.ref_account(env()->ram_session_cap()); env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA); - /* - * Register default exception handler by specifying an invalid - * thread capability. - */ + /* register default exception handler */ cpu.exception_handler(Thread_capability(), sigh); + + /* register handler for unresolvable page faults */ + rm.fault_handler(sigh); } } _resources; Genode::Rom_connection _elf; - - Genode::Child _child; - + Genode::Child _child; Genode::Parent_service _log_service; public: @@ -92,11 +114,11 @@ class Test_child : public Genode::Child_policy }; -int main(int argc, char **argv) +void failsafe_child_test() { using namespace Genode; - printf("--- failsafe test started ---\n"); + printf("-- exercise failure detection of immediate child --\n"); /* * Entry point used for serving the parent interface @@ -106,27 +128,26 @@ int main(int argc, char **argv) Rpc_entrypoint ep(&cap, STACK_SIZE, "child"); /* - * Signal receiver of CPU-session exception signals + * Signal receiver and signal context for signals originating from the + * children's CPU-session and RM session. */ - static Signal_receiver sig_rec; + Signal_receiver sig_rec; + Signal_context sig_ctx; + /* + * Iteratively start a faulting program and detect the faults + */ for (int i = 0; i < 5; i++) { PLOG("create child %d", i); - Signal_context sig_ctx; - Signal_context_capability exception_sigh = sig_rec.manage(&sig_ctx); + /* create and start child process */ + Test_child child(ep, "test-segfault", sig_rec.manage(&sig_ctx)); - Test_child child(ep, "test-segfault", exception_sigh); + PLOG("wait_for_signal"); - Signal s = sig_rec.wait_for_signal(); - if (s.num() && s.context() == &sig_ctx) { - PLOG("got exception for child %d", i); - } else { - PERR("got unexpected signal while waiting for child %d", i); - return -2; - } + wait_for_signal_for_context(sig_rec, sig_ctx); sig_rec.dissolve(&sig_ctx); @@ -137,6 +158,130 @@ int main(int argc, char **argv) */ } + printf("\n"); +} + + +/****************************************************************** + ** Test for detecting failures in a child started by the loader ** + ******************************************************************/ + +void failsafe_loader_child_test() +{ + using namespace Genode; + + printf("-- exercise failure detection of loaded child --\n"); + + /* + * Signal receiver and signal context for receiving faults originating from + * the loader subsystem. + */ + static Signal_receiver sig_rec; + Signal_context sig_ctx; + + for (int i = 0; i < 5; i++) { + + PLOG("create loader session %d", i); + + Loader::Connection loader(1024*1024); + + /* register fault handler at loader session */ + loader.fault_sigh(sig_rec.manage(&sig_ctx)); + + /* start subsystem */ + loader.start("test-segfault"); + + wait_for_signal_for_context(sig_rec, sig_ctx); + + sig_rec.dissolve(&sig_ctx); + } + + printf("\n"); +} + + +/*********************************************************************** + ** Test for detecting failures in a grandchild started by the loader ** + ***********************************************************************/ + +void failsafe_loader_grand_child_test() +{ + using namespace Genode; + + printf("-- exercise failure detection of loaded grand child --\n"); + + /* + * Signal receiver and signal context for receiving faults originating from + * the loader subsystem. + */ + static Signal_receiver sig_rec; + Signal_context sig_ctx; + + for (int i = 0; i < 5; i++) { + + PLOG("create loader session %d", i); + + Loader::Connection loader(2024*1024); + + /* + * Install init config for subsystem into the loader session + */ + char const *config = + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + + size_t config_size = strlen(config); + + Dataspace_capability config_ds = + loader.alloc_rom_module("config", config_size); + + char *config_ds_addr = env()->rm_session()->attach(config_ds); + memcpy(config_ds_addr, config, config_size); + env()->rm_session()->detach(config_ds_addr); + + loader.commit_rom_module("config"); + + /* register fault handler at loader session */ + loader.fault_sigh(sig_rec.manage(&sig_ctx)); + + /* start subsystem */ + loader.start("init", "init"); + + wait_for_signal_for_context(sig_rec, sig_ctx); + + sig_rec.dissolve(&sig_ctx); + } + + printf("\n"); +} + + +/****************** + ** Main program ** + ******************/ + +int main(int argc, char **argv) +{ + using namespace Genode; + + printf("--- failsafe test started ---\n"); + + failsafe_child_test(); + + failsafe_loader_child_test(); + + failsafe_loader_grand_child_test(); + printf("--- finished failsafe test ---\n"); return 0; }