blob: 588e07982ce3f7d8b73f57598194abbea6aeff7c [file] [log] [blame]
// Copyright 2024 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 "src/devices/block/drivers/ramdisk/v2/ramdisk-controller.h"
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/zx/vmo.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <string>
#include <safemath/checked_math.h>
#include "src/devices/block/drivers/ramdisk/v2/ramdisk.h"
namespace ramdisk_v2 {
namespace fio = fuchsia_io;
RamdiskController::RamdiskController(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: DriverBase("ramctl-v2", std::move(start_args), std::move(driver_dispatcher)) {}
zx::result<> RamdiskController::Start() {
fuchsia_hardware_ramdisk::Service::InstanceHandler handler(
{.controller = bind_handler(dispatcher())});
if (zx::result result =
outgoing()->AddService<fuchsia_hardware_ramdisk::Service>(std::move(handler));
result.is_error()) {
return result;
}
inspector().Health().Ok();
node_client_.Bind(std::move(node()), dispatcher());
return zx::ok();
}
void RamdiskController::Create(CreateRequestView request, CreateCompleter::Sync& completer) {
uint32_t block_size =
request->has_block_size() ? request->block_size() : zx_system_get_page_size();
if (block_size == 0) {
completer.Reply(zx::error(ZX_ERR_INVALID_ARGS));
return;
}
uint64_t block_count;
zx::vmo vmo;
if (request->has_vmo()) {
vmo = std::move(request->vmo());
if (request->has_block_count()) {
block_count = request->block_count();
if (!safemath::CheckMul<uint64_t>(block_size, block_count).IsValid()) {
completer.Reply(zx::error(ZX_ERR_INVALID_ARGS));
return;
}
} else {
uint64_t vmo_size;
if (zx_status_t status = vmo.get_size(&vmo_size); status != ZX_OK) {
completer.Reply(zx::error(status));
return;
}
block_count = vmo_size / block_size;
}
} else {
if (!request->has_block_count()) {
completer.Reply(zx::error(ZX_ERR_INVALID_ARGS));
return;
}
block_count = request->block_count();
uint64_t size;
if (!safemath::CheckMul<uint64_t>(block_size, block_count).AssignIfValid(&size)) {
completer.Reply(zx::error(ZX_ERR_INVALID_ARGS));
return;
}
if (zx_status_t status = zx::vmo::create(size, 0, &vmo); status != ZX_OK) {
completer.Reply(zx::error(status));
return;
}
}
component::OutgoingDirectory outgoing(dispatcher());
fidl::ClientEnd<fio::Directory> client;
zx::result server = fidl::CreateEndpoints(&client);
if (server.is_error()) {
completer.Reply(server.take_error());
return;
}
if (zx::result result = outgoing.Serve(*std::move(server)); result.is_error()) {
completer.Reply(result.take_error());
return;
}
block_server::PartitionInfo partition_info = {
.start_block = 0,
.block_count = block_count,
.block_size = block_size,
};
if (request->has_max_transfer_blocks()) {
partition_info.max_transfer_size = request->max_transfer_blocks() * block_size;
}
if (request->has_device_flags()) {
partition_info.device_flags = static_cast<uint32_t>(request->device_flags());
}
static std::atomic<int> counter = 0;
int id = counter.fetch_add(1);
if (request->has_type_guid())
memcpy(partition_info.type_guid, request->type_guid().value.data(), 16);
zx::eventpair endpoint0, endpoint1;
if (zx_status_t status = zx::eventpair::create(0, &endpoint0, &endpoint1); status != ZX_OK) {
completer.Reply(zx::error(status));
return;
}
zx_handle_t handle = endpoint1.get();
auto waiter = std::make_unique<async::WaitOnce>(handle, ZX_EVENTPAIR_PEER_CLOSED, 0);
waiter->Begin(dispatcher(), [this, id, endpoint1 = std::move(endpoint1)](
async_dispatcher_t*, async::WaitOnce*, zx_status_t,
const zx_packet_signal_t*) { ramdisks_.erase(id); });
if (zx::result ramdisk =
Ramdisk::Create(this, dispatcher(), std::move(vmo), partition_info, std::move(outgoing),
id, request->has_publish() ? request->publish() : false);
ramdisk.is_error()) {
completer.Reply(ramdisk.take_error());
} else {
ramdisks_.emplace(id, std::make_pair(*std::move(ramdisk), std::move(waiter)));
completer.ReplySuccess(std::move(client), std::move(endpoint0));
}
}
} // namespace ramdisk_v2
FUCHSIA_DRIVER_EXPORT(ramdisk_v2::RamdiskController);