| // Copyright 2019 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 "svcfs-service.h" |
| |
| #include <fuchsia/boot/c/fidl.h> |
| #include <lib/fidl-async/bind.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| #include <lib/zx/job.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls/log.h> |
| |
| #include "util.h" |
| |
| namespace { |
| |
| struct ArgumentsData { |
| zx::vmo vmo; |
| size_t size; |
| }; |
| |
| zx_status_t ArgumentsGet(void* ctx, fidl_txn_t* txn) { |
| auto data = static_cast<const ArgumentsData*>(ctx); |
| zx::vmo dup; |
| zx_status_t status = data->vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to duplicate boot arguments VMO: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_ArgumentsGet_reply(txn, dup.release(), data->size); |
| } |
| |
| constexpr fuchsia_boot_Arguments_ops kArgumentsOps = { |
| .Get = ArgumentsGet, |
| }; |
| |
| zx_status_t FactoryItemsGet(void* ctx, uint32_t extra, fidl_txn_t* txn) { |
| auto map = static_cast<bootsvc::FactoryItemMap*>(ctx); |
| auto it = map->find(extra); |
| if (it == map->end()) { |
| return fuchsia_boot_FactoryItemsGet_reply(txn, ZX_HANDLE_INVALID, 0); |
| } |
| |
| const zx::vmo& vmo = it->second.vmo; |
| uint32_t length = it->second.length; |
| zx::vmo payload; |
| zx_status_t status = |
| vmo.duplicate(ZX_DEFAULT_VMO_RIGHTS & ~(ZX_RIGHT_WRITE | ZX_RIGHT_SET_PROPERTY), &payload); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to duplicate handle for factory item VMO: %s", |
| zx_status_get_string(status)); |
| return status; |
| } |
| |
| return fuchsia_boot_FactoryItemsGet_reply(txn, payload.release(), length); |
| } |
| |
| constexpr fuchsia_boot_FactoryItems_ops kFactoryItemsOps = { |
| .Get = FactoryItemsGet, |
| }; |
| |
| struct ItemsData { |
| zx::vmo vmo; |
| bootsvc::ItemMap map; |
| }; |
| |
| zx_status_t ItemsGet(void* ctx, uint32_t type, uint32_t extra, fidl_txn_t* txn) { |
| auto data = static_cast<const ItemsData*>(ctx); |
| auto it = data->map.find(bootsvc::ItemKey{type, extra}); |
| if (it == data->map.end()) { |
| return fuchsia_boot_ItemsGet_reply(txn, ZX_HANDLE_INVALID, 0); |
| } |
| auto& item = it->second; |
| auto buf = std::make_unique<uint8_t[]>(item.length); |
| zx_status_t status = data->vmo.read(buf.get(), item.offset, item.length); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to read from boot image VMO: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| zx::vmo payload; |
| status = zx::vmo::create(item.length, 0, &payload); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to create payload VMO: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| status = payload.write(buf.get(), 0, item.length); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to write to payload VMO: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_ItemsGet_reply(txn, payload.release(), item.length); |
| } |
| |
| constexpr fuchsia_boot_Items_ops kItemsOps = { |
| .Get = ItemsGet, |
| }; |
| |
| zx_status_t ReadOnlyLogGet(void* ctx, fidl_txn_t* txn) { |
| auto root_resource = static_cast<const zx::resource*>(ctx); |
| zx::debuglog ret; |
| zx_status_t status = zx::debuglog::create(*root_resource, ZX_LOG_FLAG_READABLE, &ret); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to create readable kernel log: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| |
| // Drop write right. |
| status = ret.replace((ZX_DEFAULT_LOG_RIGHTS & (~ZX_RIGHT_WRITE)) | ZX_RIGHT_READ, &ret); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to drop write from readable kernel log: %s\n", |
| zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_ReadOnlyLogGet_reply(txn, ret.release()); |
| } |
| |
| constexpr fuchsia_boot_ReadOnlyLog_ops kReadOnlyLogOps = { |
| .Get = ReadOnlyLogGet, |
| }; |
| |
| zx_status_t WriteOnlyLogGet(void* ctx, fidl_txn_t* txn) { |
| auto log = static_cast<const zx::debuglog*>(ctx); |
| zx::debuglog dup; |
| zx_status_t status = log->duplicate(ZX_RIGHT_SAME_RIGHTS, &dup); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to duplicate writable kernel log: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_WriteOnlyLogGet_reply(txn, dup.release()); |
| } |
| |
| constexpr fuchsia_boot_WriteOnlyLog_ops kWriteOnlyLogOps = { |
| .Get = WriteOnlyLogGet, |
| }; |
| |
| zx_status_t RootJobGet(void* ctx, fidl_txn_t* txn) { |
| zx::job dup; |
| zx_status_t status = zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &dup); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to duplicate root job: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_RootJobGet_reply(txn, dup.release()); |
| } |
| |
| constexpr fuchsia_boot_RootJob_ops kRootJobOps = { |
| .Get = RootJobGet, |
| }; |
| |
| zx_status_t RootJobForInspectGet(void* ctx, fidl_txn_t* txn) { |
| zx::job dup; |
| zx_status_t status = |
| zx::job::default_job()->duplicate(ZX_RIGHT_INSPECT | ZX_RIGHT_ENUMERATE | ZX_RIGHT_DUPLICATE | |
| ZX_RIGHT_TRANSFER | ZX_RIGHT_GET_PROPERTY, |
| &dup); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to duplicate root job: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_RootJobForInspectGet_reply(txn, dup.release()); |
| } |
| |
| constexpr fuchsia_boot_RootJobForInspect_ops kRootJobForInspectOps = { |
| .Get = RootJobForInspectGet, |
| }; |
| |
| zx_status_t RootResourceGet(void* ctx, fidl_txn_t* txn) { |
| auto root_resource_handle = static_cast<const zx::resource*>(ctx); |
| zx::resource root_resource_dup; |
| zx_status_t status = root_resource_handle->duplicate(ZX_RIGHT_SAME_RIGHTS, &root_resource_dup); |
| if (status != ZX_OK) { |
| printf("bootsvc: Failed to duplicate root resource handle: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| return fuchsia_boot_RootResourceGet_reply(txn, root_resource_dup.release()); |
| } |
| |
| constexpr fuchsia_boot_RootResource_ops kRootResourceOps = { |
| .Get = RootResourceGet, |
| }; |
| |
| } // namespace |
| |
| namespace bootsvc { |
| |
| fbl::RefPtr<SvcfsService> SvcfsService::Create(async_dispatcher_t* dispatcher) { |
| return fbl::AdoptRef(new SvcfsService(dispatcher)); |
| } |
| |
| SvcfsService::SvcfsService(async_dispatcher_t* dispatcher) |
| : vfs_(dispatcher), root_(fbl::MakeRefCounted<fs::PseudoDir>()) {} |
| |
| void SvcfsService::AddService(const char* service_name, fbl::RefPtr<fs::Service> service) { |
| root_->AddEntry(service_name, std::move(service)); |
| } |
| |
| zx_status_t SvcfsService::CreateRootConnection(zx::channel* out) { |
| return CreateVnodeConnection(&vfs_, root_, fs::Rights::ReadWrite(), out); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateArgumentsService(async_dispatcher_t* dispatcher, zx::vmo vmo, |
| uint64_t size) { |
| ArgumentsData data{std::move(vmo), size}; |
| return fbl::MakeRefCounted<fs::Service>( |
| [dispatcher, data = std::move(data)](zx::channel channel) mutable { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_Arguments_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, &data, &kArgumentsOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateFactoryItemsService(async_dispatcher_t* dispatcher, |
| FactoryItemMap map) { |
| return fbl::MakeRefCounted<fs::Service>( |
| [dispatcher, map = std::move(map)](zx::channel channel) mutable { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_FactoryItems_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, &map, &kFactoryItemsOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateItemsService(async_dispatcher_t* dispatcher, zx::vmo vmo, |
| ItemMap map) { |
| ItemsData data{std::move(vmo), std::move(map)}; |
| return fbl::MakeRefCounted<fs::Service>( |
| [dispatcher, data = std::move(data)](zx::channel channel) mutable { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_Items_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, &data, &kItemsOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateReadOnlyLogService(async_dispatcher_t* dispatcher, |
| const zx::resource& root_resource) { |
| return fbl::MakeRefCounted<fs::Service>([dispatcher, &root_resource](zx::channel channel) { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_ReadOnlyLog_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, |
| const_cast<zx::resource*>(&root_resource), &kReadOnlyLogOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateWriteOnlyLogService(async_dispatcher_t* dispatcher, |
| const zx::debuglog& log) { |
| return fbl::MakeRefCounted<fs::Service>([dispatcher, &log](zx::channel channel) { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_WriteOnlyLog_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, const_cast<zx::debuglog*>(&log), |
| &kWriteOnlyLogOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateRootJobService(async_dispatcher_t* dispatcher) { |
| return fbl::MakeRefCounted<fs::Service>([dispatcher](zx::channel channel) { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_RootJob_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, nullptr, &kRootJobOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateRootJobForInspectService(async_dispatcher_t* dispatcher) { |
| return fbl::MakeRefCounted<fs::Service>([dispatcher](zx::channel channel) { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_RootJobForInspect_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, nullptr, &kRootJobForInspectOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> CreateRootResourceService(async_dispatcher_t* dispatcher, |
| zx::resource root_resource) { |
| return fbl::MakeRefCounted<fs::Service>( |
| [dispatcher, resource = std::move(root_resource)](zx::channel channel) mutable { |
| auto dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_RootResource_dispatch); |
| return fidl_bind(dispatcher, channel.release(), dispatch, &resource, &kRootResourceOps); |
| }); |
| } |
| |
| fbl::RefPtr<fs::Service> KernelStatsImpl::CreateService(async_dispatcher_t* dispatcher) { |
| return fbl::MakeRefCounted<fs::Service>([dispatcher, this](zx::channel channel) mutable { |
| return fidl::Bind(dispatcher, std::move(channel), this); |
| }); |
| } |
| |
| void KernelStatsImpl::GetMemoryStats(GetMemoryStatsCompleter::Sync completer) { |
| zx_info_kmem_stats_t mem_stats; |
| zx_status_t status = root_resource_handle_.get_info( |
| ZX_INFO_KMEM_STATS, &mem_stats, sizeof(zx_info_kmem_stats_t), nullptr, nullptr); |
| if (status != ZX_OK) { |
| completer.Close(status); |
| return; |
| } |
| auto builder = llcpp::fuchsia::kernel::MemoryStats::Build(); |
| builder.set_total_bytes(&mem_stats.total_bytes); |
| builder.set_free_bytes(&mem_stats.free_bytes); |
| builder.set_wired_bytes(&mem_stats.wired_bytes); |
| builder.set_total_heap_bytes(&mem_stats.total_heap_bytes); |
| builder.set_free_heap_bytes(&mem_stats.free_heap_bytes); |
| builder.set_vmo_bytes(&mem_stats.vmo_bytes); |
| builder.set_mmu_overhead_bytes(&mem_stats.mmu_overhead_bytes); |
| builder.set_ipc_bytes(&mem_stats.ipc_bytes); |
| builder.set_other_bytes(&mem_stats.other_bytes); |
| completer.Reply(builder.view()); |
| } |
| |
| void KernelStatsImpl::GetCpuStats(GetCpuStatsCompleter::Sync completer) { |
| zx_info_cpu_stats_t cpu_stats[ZX_CPU_SET_MAX_CPUS]; |
| size_t actual, available; |
| zx_status_t status = root_resource_handle_.get_info( |
| ZX_INFO_CPU_STATS, &cpu_stats, sizeof(zx_info_cpu_stats_t) * ZX_CPU_SET_MAX_CPUS, &actual, |
| &available); |
| if (status != ZX_OK) { |
| completer.Close(status); |
| return; |
| } |
| llcpp::fuchsia::kernel::CpuStats stats; |
| stats.actual_num_cpus = actual; |
| llcpp::fuchsia::kernel::PerCpuStats per_cpu_stats[available]; |
| fbl::Vector<std::unique_ptr<llcpp::fuchsia::kernel::PerCpuStats::Builder>> builders; |
| stats.per_cpu_stats = fidl::VectorView(per_cpu_stats, available); |
| for (uint32_t cpu_num = 0; cpu_num < available; ++cpu_num) { |
| builders.push_back(std::make_unique<llcpp::fuchsia::kernel::PerCpuStats::Builder>( |
| llcpp::fuchsia::kernel::PerCpuStats::Build())); |
| auto& builder = builders[cpu_num]; |
| auto& cpu_stat = cpu_stats[cpu_num]; |
| builder->set_cpu_number(&cpu_stat.cpu_number); |
| builder->set_flags(&cpu_stat.flags); |
| builder->set_idle_time(&cpu_stat.idle_time); |
| builder->set_reschedules(&cpu_stat.reschedules); |
| builder->set_context_switches(&cpu_stat.context_switches); |
| builder->set_irq_preempts(&cpu_stat.irq_preempts); |
| builder->set_yields(&cpu_stat.yields); |
| builder->set_ints(&cpu_stat.ints); |
| builder->set_timer_ints(&cpu_stat.timer_ints); |
| builder->set_timers(&cpu_stat.timers); |
| builder->set_page_faults(&cpu_stat.page_faults); |
| builder->set_exceptions(&cpu_stat.exceptions); |
| builder->set_syscalls(&cpu_stat.syscalls); |
| builder->set_reschedule_ipis(&cpu_stat.reschedule_ipis); |
| builder->set_generic_ipis(&cpu_stat.generic_ipis); |
| per_cpu_stats[cpu_num] = builder->view(); |
| } |
| completer.Reply(stats); |
| } |
| |
| } // namespace bootsvc |