| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/boot/cpp/fidl.h> |
| #include <getopt.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/devmgr-launcher/processargs.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fit/optional.h> |
| #include <lib/zx/event.h> |
| #include <lib/zx/port.h> |
| #include <lib/zx/resource.h> |
| #include <lib/zx/vmo.h> |
| #include <threads.h> |
| #include <zircon/device/vfs.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/syscalls/policy.h> |
| #include <zircon/types.h> |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <memory> |
| |
| #include <fbl/string_printf.h> |
| #include <fs/managed_vfs.h> |
| #include <fs/pseudo_dir.h> |
| #include <fs/remote_dir.h> |
| #include <fs/vfs.h> |
| #include <fs/vmo_file.h> |
| |
| #include "component_lifecycle.h" |
| #include "coordinator.h" |
| #include "devfs.h" |
| #include "driver_host_loader_service.h" |
| #include "fdio.h" |
| #include "src/devices/lib/log/log.h" |
| #include "src/sys/lib/stdout-to-debuglog/cpp/stdout-to-debuglog.h" |
| #include "system_instance.h" |
| |
| namespace { |
| |
| // These are helpers for getting sets of parameters over FIDL |
| struct DriverManagerParams { |
| bool driver_host_asan; |
| bool driver_host_strict_linking; |
| bool log_to_debuglog; |
| bool require_system; |
| bool suspend_timeout_fallback; |
| bool verbose; |
| }; |
| |
| DriverManagerParams GetDriverManagerParams(llcpp::fuchsia::boot::Arguments::SyncClient& client) { |
| llcpp::fuchsia::boot::BoolPair bool_req[]{ |
| // TODO(bwb): remove this or figure out how to make it work |
| {"devmgr.devhost.asan", false}, |
| {"devmgr.devhost.strict-linking", false}, |
| {"devmgr.log-to-debuglog", false}, |
| {"devmgr.require-system", false}, |
| // Turn it on by default. See fxb/34577 |
| {"devmgr.suspend-timeout-fallback", true}, |
| {"devmgr.verbose", false}, |
| }; |
| auto bool_resp = client.GetBools(fidl::unowned_vec(bool_req)); |
| if (!bool_resp.ok()) { |
| return {}; |
| } |
| return {bool_resp->values[0], bool_resp->values[1], bool_resp->values[2], |
| bool_resp->values[3], bool_resp->values[4], bool_resp->values[5]}; |
| } |
| |
| static const std::string kRootJobPath = "/svc/" + std::string(fuchsia::boot::RootJob::Name_); |
| static const std::string kRootResourcePath = |
| "/svc/" + std::string(fuchsia::boot::RootResource::Name_); |
| |
| // Get the root job from the root job service. |
| zx_status_t get_root_job(zx::job* root_job) { |
| fuchsia::boot::RootJobSyncPtr root_job_ptr; |
| zx_status_t status = fdio_service_connect(kRootJobPath.c_str(), |
| root_job_ptr.NewRequest().TakeChannel().release()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return root_job_ptr->Get(root_job); |
| } |
| |
| // Get the root resource from the root resource service. Not receiving the |
| // startup handle is logged, but not fatal. In test environments, it would not |
| // be present. |
| zx_status_t get_root_resource(zx::resource* root_resource) { |
| fuchsia::boot::RootResourceSyncPtr root_resource_ptr; |
| zx_status_t status = fdio_service_connect(kRootResourcePath.c_str(), |
| root_resource_ptr.NewRequest().TakeChannel().release()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return root_resource_ptr->Get(root_resource); |
| } |
| |
| void ParseArgs(int argc, char** argv, DevmgrArgs* out) { |
| enum { |
| kDriverSearchPath, |
| kLoadDriver, |
| kSysDeviceDriver, |
| kNoStartSvchost, |
| kDisableBlockWatcher, |
| kDisableNetsvc, |
| kLogToDebuglog, |
| }; |
| option options[] = { |
| {"driver-search-path", required_argument, nullptr, kDriverSearchPath}, |
| {"load-driver", required_argument, nullptr, kLoadDriver}, |
| {"sys-device-driver", required_argument, nullptr, kSysDeviceDriver}, |
| {"no-start-svchost", no_argument, nullptr, kNoStartSvchost}, |
| {"disable-block-watcher", no_argument, nullptr, kDisableBlockWatcher}, |
| {"disable-netsvc", no_argument, nullptr, kDisableNetsvc}, |
| {"log-to-debuglog", no_argument, nullptr, kLogToDebuglog}, |
| }; |
| |
| auto print_usage_and_exit = [options]() { |
| printf("driver_manager: supported arguments:\n"); |
| for (const auto& option : options) { |
| printf(" --%s\n", option.name); |
| } |
| abort(); |
| }; |
| |
| auto check_not_duplicated = [print_usage_and_exit](const char* arg) { |
| if (arg != nullptr) { |
| printf("driver_manager: duplicated argument\n"); |
| print_usage_and_exit(); |
| } |
| }; |
| |
| // Reset the args state |
| *out = DevmgrArgs(); |
| |
| int opt; |
| while ((opt = getopt_long(argc, argv, "", options, nullptr)) != -1) { |
| switch (opt) { |
| case kDriverSearchPath: |
| out->driver_search_paths.push_back(optarg); |
| break; |
| case kLoadDriver: |
| out->load_drivers.push_back(optarg); |
| break; |
| case kSysDeviceDriver: |
| check_not_duplicated(out->sys_device_driver); |
| out->sys_device_driver = optarg; |
| break; |
| case kNoStartSvchost: |
| out->start_svchost = false; |
| break; |
| case kDisableBlockWatcher: |
| out->disable_block_watcher = true; |
| break; |
| case kDisableNetsvc: |
| out->disable_netsvc = true; |
| break; |
| case kLogToDebuglog: |
| out->log_to_debuglog = true; |
| break; |
| default: |
| print_usage_and_exit(); |
| } |
| } |
| } |
| |
| zx_status_t CreateDriverHostJob(const zx::job& root_job, zx::job* driver_host_job_out) { |
| zx::job driver_host_job; |
| zx_status_t status = zx::job::create(root_job, 0u, &driver_host_job); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Unable to create driver_host job: %s", zx_status_get_string(status)); |
| return status; |
| } |
| static const zx_policy_basic_v2_t policy[] = { |
| {ZX_POL_BAD_HANDLE, ZX_POL_ACTION_ALLOW_EXCEPTION, ZX_POL_OVERRIDE_DENY}, |
| }; |
| status = driver_host_job.set_policy(ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC_V2, &policy, |
| fbl::count_of(policy)); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to set driver_host job policy: %s", zx_status_get_string(status)); |
| return status; |
| } |
| status = driver_host_job.set_property(ZX_PROP_NAME, "zircon-drivers", 15); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to set driver_host job property: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| *driver_host_job_out = std::move(driver_host_job); |
| return ZX_OK; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| zx_status_t status = StdoutToDebuglog::Init(); |
| if (status != ZX_OK) { |
| LOGF(INFO, "Failed to redirect stdout to debuglog, assuming test environment and continuing"); |
| } |
| |
| zx::channel local, remote; |
| status = zx::channel::create(0, &local, &remote); |
| if (status != ZX_OK) { |
| return status; |
| } |
| auto path = fbl::StringPrintf("/svc/%s", llcpp::fuchsia::boot::Arguments::Name); |
| status = fdio_service_connect(path.data(), remote.release()); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to get boot arguments service handle: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| auto boot_args = llcpp::fuchsia::boot::Arguments::SyncClient{std::move(local)}; |
| auto driver_manager_params = GetDriverManagerParams(boot_args); |
| DevmgrArgs devmgr_args; |
| ParseArgs(argc, argv, &devmgr_args); |
| |
| if (driver_manager_params.verbose) { |
| FX_LOG_SET_VERBOSITY(1); |
| } |
| if (driver_manager_params.log_to_debuglog || devmgr_args.log_to_debuglog) { |
| zx_status_t status = log_to_debuglog(); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to reconfigure logger to use debuglog: %s", zx_status_get_string(status)); |
| return status; |
| } |
| } |
| // Set up the default values for our arguments if they weren't given. |
| if (devmgr_args.driver_search_paths.size() == 0) { |
| devmgr_args.driver_search_paths.push_back("/boot/driver"); |
| } |
| if (devmgr_args.sys_device_driver == nullptr) { |
| devmgr_args.sys_device_driver = "/boot/driver/platform-bus.so"; |
| } |
| |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| CoordinatorConfig config{}; |
| SystemInstance system_instance; |
| config.dispatcher = loop.dispatcher(); |
| config.boot_args = &boot_args; |
| config.require_system = driver_manager_params.require_system; |
| config.asan_drivers = driver_manager_params.driver_host_asan; |
| config.suspend_fallback = driver_manager_params.suspend_timeout_fallback; |
| config.log_to_debuglog = driver_manager_params.log_to_debuglog || devmgr_args.log_to_debuglog; |
| config.verbose = driver_manager_params.verbose; |
| config.disable_netsvc = devmgr_args.disable_netsvc; |
| config.fs_provider = &system_instance; |
| |
| // TODO(ZX-4178): Remove all uses of the root resource. |
| status = get_root_resource(&config.root_resource); |
| if (status != ZX_OK) { |
| LOGF(INFO, "Failed to get root resource, assuming test environment and continuing"); |
| } |
| // TODO(ZX-4177): Remove all uses of the root job. |
| zx::job root_job; |
| status = get_root_job(&root_job); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to get root job: %s", zx_status_get_string(status)); |
| return status; |
| } |
| status = CreateDriverHostJob(root_job, &config.driver_host_job); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to create driver_host job: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| zx_handle_t oom_event; |
| status = zx_system_get_event(root_job.get(), ZX_SYSTEM_EVENT_OUT_OF_MEMORY, &oom_event); |
| if (status != ZX_OK) { |
| LOGF(INFO, "Failed to get OOM event, assuming test environment and continuing"); |
| } else { |
| config.oom_event = zx::event(oom_event); |
| } |
| |
| Coordinator coordinator(std::move(config)); |
| |
| // Services offered to the rest of the system. |
| svc::Outgoing outgoing{loop.dispatcher()}; |
| status = coordinator.InitOutgoingServices(outgoing.svc_dir()); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to initialize outgoing services: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| // Check if whatever launched devcoordinator gave a channel to be connected to the |
| // outgoing services directory. This is for use in tests to let the test environment see |
| // outgoing services. |
| zx::channel outgoing_svc_dir_client( |
| zx_take_startup_handle(DEVMGR_LAUNCHER_OUTGOING_SERVICES_HND)); |
| if (outgoing_svc_dir_client.is_valid()) { |
| status = outgoing.Serve(std::move(outgoing_svc_dir_client)); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to bind outgoing services: %s", zx_status_get_string(status)); |
| return status; |
| } |
| } |
| |
| status = coordinator.InitCoreDevices(devmgr_args.sys_device_driver); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to initialize core devices: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| devfs_init(coordinator.root_device(), loop.dispatcher()); |
| devfs_publish(coordinator.root_device(), coordinator.misc_device()); |
| devfs_publish(coordinator.root_device(), coordinator.sys_device()); |
| devfs_publish(coordinator.root_device(), coordinator.test_device()); |
| devfs_connect_diagnostics(coordinator.inspect_manager().diagnostics_channel()); |
| |
| // Check if whatever launched devmgr gave a channel to be connected to /dev. |
| // This is for use in tests to let the test environment see devfs. |
| zx::channel devfs_client(zx_take_startup_handle(DEVMGR_LAUNCHER_DEVFS_ROOT_HND)); |
| if (devfs_client.is_valid()) { |
| fdio_service_clone_to(devfs_root_borrow()->get(), devfs_client.release()); |
| } |
| |
| // Check if whatever launched devmgr gave a channel for component lifecycle events |
| zx::channel component_lifecycle_request(zx_take_startup_handle(PA_LIFECYCLE)); |
| ZX_ASSERT(component_lifecycle_request.is_valid()); |
| if (component_lifecycle_request.is_valid()) { |
| status = devmgr::ComponentLifecycleServer::Create(loop.dispatcher(), &coordinator, |
| std::move(component_lifecycle_request)); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "driver_manager: Cannot create componentlifecycleserver: %s\n", |
| zx_status_get_string(status)); |
| return status; |
| } |
| } |
| |
| status = system_instance.CreateSvcJob(root_job); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = system_instance.PrepareChannels(); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to create other system channels: %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| if (devmgr_args.start_svchost) { |
| zx::channel root_server, root_client; |
| status = zx::channel::create(0, &root_server, &root_client); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = outgoing.Serve(std::move(root_server)); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to bind outgoing services: %s", zx_status_get_string(status)); |
| return status; |
| } |
| status = system_instance.StartSvchost(root_job, root_client, |
| driver_manager_params.require_system, &coordinator); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to start svchost: %s", zx_status_get_string(status)); |
| return status; |
| } |
| } else { |
| status = system_instance.ReuseExistingSvchost(); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to reuse existing svchost: %s", zx_status_get_string(status)); |
| return status; |
| } |
| } |
| |
| system_instance.devmgr_vfs_init(); |
| |
| thrd_t t; |
| |
| auto pwrbtn_starter_args = std::make_unique<SystemInstance::ServiceStarterArgs>(); |
| pwrbtn_starter_args->instance = &system_instance; |
| pwrbtn_starter_args->coordinator = &coordinator; |
| int ret = thrd_create_with_name(&t, SystemInstance::pwrbtn_monitor_starter, |
| pwrbtn_starter_args.release(), "pwrbtn-monitor-starter"); |
| if (ret != thrd_success) { |
| LOGF(ERROR, "Failed to create pwrbtn monitor starter thread: %d", ret); |
| return ret; |
| } |
| thrd_detach(t); |
| |
| system_instance.start_console_shell(boot_args); |
| |
| auto service_starter_args = std::make_unique<SystemInstance::ServiceStarterArgs>(); |
| service_starter_args->instance = &system_instance; |
| service_starter_args->coordinator = &coordinator; |
| ret = thrd_create_with_name(&t, SystemInstance::service_starter, service_starter_args.release(), |
| "service-starter"); |
| if (ret != thrd_success) { |
| LOGF(ERROR, "Failed to create service starter thread: %d", ret); |
| return ret; |
| } |
| thrd_detach(t); |
| |
| if (driver_manager_params.driver_host_strict_linking) { |
| std::unique_ptr<DriverHostLoaderService> loader_service; |
| status = DriverHostLoaderService::Create(loop.dispatcher(), &system_instance, &loader_service); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to create loader service: %s", zx_status_get_string(status)); |
| return status; |
| } |
| coordinator.set_loader_service_connector([ls = std::move(loader_service)](zx::channel* c) { |
| zx_status_t status = ls->Connect(c); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to add driver_host loader connection: %s", |
| zx_status_get_string(status)); |
| } |
| return status; |
| }); |
| } else { |
| coordinator.set_loader_service_connector([&system_instance](zx::channel* c) { |
| zx_status_t status = system_instance.clone_fshost_ldsvc(c); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to clone fshost loader for driver_host: %s", |
| zx_status_get_string(status)); |
| } |
| return status; |
| }); |
| } |
| |
| for (const char* path : devmgr_args.driver_search_paths) { |
| find_loadable_drivers(path, fit::bind_member(&coordinator, &Coordinator::DriverAddedInit)); |
| } |
| for (const char* driver : devmgr_args.load_drivers) { |
| load_driver(driver, fit::bind_member(&coordinator, &Coordinator::DriverAddedInit)); |
| } |
| |
| if (coordinator.require_system() && !coordinator.system_loaded()) { |
| LOGF(INFO, "Full system required, ignoring fallback drivers until '/system' is loaded"); |
| } else { |
| coordinator.UseFallbackDrivers(); |
| } |
| |
| coordinator.PrepareProxy(coordinator.sys_device(), nullptr); |
| coordinator.PrepareProxy(coordinator.test_device(), nullptr); |
| // Initial bind attempt for drivers enumerated at startup. |
| coordinator.BindDrivers(); |
| |
| // Expose /dev directory for use in sysinfo service; specifically to connect to /dev/sys/platform |
| auto outgoing_dir = fbl::MakeRefCounted<fs::PseudoDir>(); |
| outgoing_dir->AddEntry("dev", fbl::MakeRefCounted<fs::RemoteDir>(system_instance.CloneFs("dev"))); |
| outgoing_dir->AddEntry("svc", fbl::MakeRefCounted<fs::RemoteDir>(system_instance.CloneFs("svc"))); |
| |
| fs::ManagedVfs outgoing_vfs = fs::ManagedVfs(loop.dispatcher()); |
| outgoing_vfs.ServeDirectory(outgoing_dir, |
| zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST))); |
| |
| coordinator.set_running(true); |
| status = loop.Run(); |
| LOGF(ERROR, "Coordinator exited unexpectedly: %s", zx_status_get_string(status)); |
| return status; |
| } |