blob: 5bdabd4625cfca463aa458b7252e3eaada7a458f [file] [log] [blame]
// 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 <fidl/fuchsia.hardware.ramdisk/cpp/wire.h>
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/driver.h>
#include <lib/zx/result.h>
#include <lib/zx/vmo.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <memory>
#include <string>
#include <bind/fuchsia/hardware/ramdisk/cpp/bind.h>
#include <ddktl/device.h>
#include "ramdisk.h"
namespace ramdisk {
namespace {
class RamdiskController;
using RamdiskControllerDeviceType =
ddk::Device<RamdiskController,
ddk::Messageable<fuchsia_hardware_ramdisk::RamdiskController>::Mixin>;
class RamdiskController : public RamdiskControllerDeviceType {
public:
explicit RamdiskController(zx_device_t* parent) : RamdiskControllerDeviceType(parent) {}
// Device Protocol
void DdkRelease() { delete this; }
private:
// FIDL Interface RamdiskController.
void Create(CreateRequestView request, CreateCompleter::Sync& completer) override;
void CreateFromVmo(CreateFromVmoRequestView request,
CreateFromVmoCompleter::Sync& completer) override;
void CreateFromVmoWithParams(CreateFromVmoWithParamsRequestView request,
CreateFromVmoWithParamsCompleter::Sync& completer) override;
// Other methods:
// ConfigureDevice returns the name of the device if successful.
zx::result<std::string> ConfigureDevice(zx::vmo vmo, uint64_t block_size, uint64_t block_count,
const uint8_t* type_guid);
zx::result<std::string> CreateFromVmoWithParamsInternal(zx::vmo vmo, uint64_t block_size,
const uint8_t* type_guid);
};
void RamdiskController::Create(CreateRequestView request, CreateCompleter::Sync& completer) {
zx::vmo vmo;
zx_status_t status =
zx::vmo::create(request->block_size * request->block_count, ZX_VMO_RESIZABLE, &vmo);
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
auto name = ConfigureDevice(std::move(vmo), request->block_size, request->block_count,
request->type_guid ? request->type_guid->value.data() : nullptr);
if (name.is_error()) {
completer.Reply(name.take_error());
return;
}
completer.ReplySuccess(fidl::StringView::FromExternal(name.value()));
}
zx::result<std::string> RamdiskController::CreateFromVmoWithParamsInternal(
zx::vmo vmo, uint64_t block_size, const uint8_t* type_guid) {
zx_info_handle_count_t handle_count_info;
zx_status_t status = vmo.get_info(ZX_INFO_HANDLE_COUNT, &handle_count_info,
sizeof(handle_count_info), nullptr, nullptr);
if (status != ZX_OK) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
zx_info_vmo_t vmo_info;
status = vmo.get_info(ZX_INFO_VMO, &vmo_info, sizeof(vmo_info), nullptr, nullptr);
if (status != ZX_OK) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
// If this is a resizeable VMO, ensure it has only one handle to prevent
// the size from changing underneath us.
if ((vmo_info.flags & ZX_INFO_VMO_RESIZABLE) && (handle_count_info.handle_count != 1)) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
uint64_t vmo_size;
status = vmo.get_size(&vmo_size);
if (status != ZX_OK) {
return zx::error(status);
}
if (block_size == 0) {
block_size = zx_system_get_page_size();
}
return ConfigureDevice(std::move(vmo), block_size, (vmo_size + block_size - 1) / block_size,
type_guid);
}
void RamdiskController::CreateFromVmo(CreateFromVmoRequestView request,
CreateFromVmoCompleter::Sync& completer) {
auto name = CreateFromVmoWithParamsInternal(std::move(request->vmo), /*block_size*/ 0,
/*type_guid*/ nullptr);
if (name.is_error()) {
completer.Reply(name.take_error());
return;
}
completer.ReplySuccess(fidl::StringView::FromExternal(name.value()));
}
void RamdiskController::CreateFromVmoWithParams(CreateFromVmoWithParamsRequestView request,
CreateFromVmoWithParamsCompleter::Sync& completer) {
auto name = CreateFromVmoWithParamsInternal(
std::move(request->vmo), request->block_size,
request->type_guid ? request->type_guid->value.data() : nullptr);
if (name.is_error()) {
completer.Reply(name.take_error());
return;
}
completer.ReplySuccess(fidl::StringView::FromExternal(name.value()));
}
zx::result<std::string> RamdiskController::ConfigureDevice(zx::vmo vmo, uint64_t block_size,
uint64_t block_count,
const uint8_t* type_guid) {
std::unique_ptr<Ramdisk> ramdev;
if (zx_status_t status =
Ramdisk::Create(zxdev(), std::move(vmo), block_size, block_count, type_guid, &ramdev);
status != ZX_OK) {
return zx::error(status);
}
if (zx_status_t status = ramdev->DdkAdd(ramdev->Name()); status != ZX_OK) {
zxlogf(ERROR, "Failed DdkAdd on ram device '%s': %s", ramdev->Name(),
zx_status_get_string(status));
ramdev.release()->DdkRelease();
return zx::error(status);
}
// RamdiskController owned by the DDK after being added successfully.
return zx::ok(std::string(ramdev.release()->Name()));
}
class RamdiskV2Stub : public ddk::Device<RamdiskV2Stub> {
public:
RamdiskV2Stub(zx_device_t* parent) : Device(parent) {}
void DdkRelease() { delete this; }
};
zx_status_t RamdiskDriverBind(void* ctx, zx_device_t* parent) {
auto ramctl = std::make_unique<RamdiskController>(parent);
if (zx_status_t status =
ramctl->DdkAdd(ddk::DeviceAddArgs("ramctl").set_flags(DEVICE_ADD_NON_BINDABLE));
status != ZX_OK) {
return status;
}
auto stub = std::make_unique<RamdiskV2Stub>(ramctl->zxdev());
const zx_device_str_prop_t props[] = {
ddk::MakeStrProperty(bind_fuchsia_hardware_ramdisk::BIND_V2, true)};
if (zx_status_t status = stub->DdkAdd(ddk::DeviceAddArgs("v2").set_str_props(props));
status != ZX_OK) {
return status;
}
stub.release();
// RamdiskController owned by the DDK after being added successfully.
[[maybe_unused]] auto ptr = ramctl.release();
return ZX_OK;
}
zx_driver_ops_t ramdisk_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = RamdiskDriverBind;
return ops;
}();
} // namespace
} // namespace ramdisk
ZIRCON_DRIVER(ramdisk, ramdisk::ramdisk_driver_ops, "zircon", "0.1");